mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Workshop - Reworked installation management
This commit is contained in:
parent
d6f1ba9aad
commit
6b4ed48d05
@ -13,6 +13,11 @@ namespace Artemis.UI.Shared.Routing;
|
|||||||
/// <typeparam name="TParam">The type of parameters the screen expects. It must have a parameterless constructor.</typeparam>
|
/// <typeparam name="TParam">The type of parameters the screen expects. It must have a parameterless constructor.</typeparam>
|
||||||
public abstract class RoutableScreen<TParam> : RoutableScreen, IRoutableScreen where TParam : new()
|
public abstract class RoutableScreen<TParam> : RoutableScreen, IRoutableScreen where TParam : new()
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the parameter source of the screen.
|
||||||
|
/// </summary>
|
||||||
|
protected ParameterSource ParameterSource { get; set; } = ParameterSource.Segment;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called while navigating to this screen.
|
/// Called while navigating to this screen.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -26,15 +31,16 @@ public abstract class RoutableScreen<TParam> : RoutableScreen, IRoutableScreen w
|
|||||||
{
|
{
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task IRoutableScreen.InternalOnNavigating(NavigationArguments args, CancellationToken cancellationToken)
|
async Task IRoutableScreen.InternalOnNavigating(NavigationArguments args, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
Func<object[], TParam> activator = GetParameterActivator();
|
Func<object[], TParam> activator = GetParameterActivator();
|
||||||
|
|
||||||
if (args.SegmentParameters.Length != _parameterPropertyCount)
|
object[] routeParameters = ParameterSource == ParameterSource.Segment ? args.SegmentParameters : args.RouteParameters;
|
||||||
throw new ArtemisRoutingException($"Did not retrieve the required amount of parameters, expects {_parameterPropertyCount}, got {args.SegmentParameters.Length}.");
|
if (routeParameters.Length != _parameterPropertyCount)
|
||||||
|
throw new ArtemisRoutingException($"Did not retrieve the required amount of parameters, expects {_parameterPropertyCount}, got {routeParameters.Length}.");
|
||||||
|
|
||||||
TParam parameters = activator(args.SegmentParameters);
|
TParam parameters = activator(routeParameters);
|
||||||
await OnNavigating(args, cancellationToken);
|
await OnNavigating(args, cancellationToken);
|
||||||
await OnNavigating(parameters, args, cancellationToken);
|
await OnNavigating(parameters, args, cancellationToken);
|
||||||
}
|
}
|
||||||
@ -97,4 +103,20 @@ public abstract class RoutableScreen<TParam> : RoutableScreen, IRoutableScreen w
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enum representing the source of parameters in the RoutableScreen class.
|
||||||
|
/// </summary>
|
||||||
|
public enum ParameterSource
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the source where parameters are obtained from the segment of the route.
|
||||||
|
/// </summary>
|
||||||
|
Segment,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the source where parameters are obtained from the entire route.
|
||||||
|
/// </summary>
|
||||||
|
Route
|
||||||
}
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Reactive.Subjects;
|
using System.Reactive.Subjects;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
@ -72,7 +73,13 @@ internal class Router : CorePropertyChanged, IRouter, IDisposable
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task Navigate(string path, RouterNavigationOptions? options = null)
|
public async Task Navigate(string path, RouterNavigationOptions? options = null)
|
||||||
{
|
{
|
||||||
path = path.ToLower().Trim(' ', '/', '\\');
|
if (path.StartsWith('/') && _currentRouteSubject.Value != null)
|
||||||
|
path = _currentRouteSubject.Value + path;
|
||||||
|
if (path.StartsWith("../") && _currentRouteSubject.Value != null)
|
||||||
|
path = NavigateUp(_currentRouteSubject.Value, path);
|
||||||
|
else
|
||||||
|
path = path.ToLower().Trim(' ', '/', '\\');
|
||||||
|
|
||||||
options ??= new RouterNavigationOptions();
|
options ??= new RouterNavigationOptions();
|
||||||
|
|
||||||
// Routing takes place on the UI thread with processing heavy tasks offloaded by the router itself
|
// Routing takes place on the UI thread with processing heavy tasks offloaded by the router itself
|
||||||
@ -216,6 +223,24 @@ internal class Router : CorePropertyChanged, IRouter, IDisposable
|
|||||||
|
|
||||||
_logger.Debug("Router disposed, should that be? Stacktrace: \r\n{StackTrace}", Environment.StackTrace);
|
_logger.Debug("Router disposed, should that be? Stacktrace: \r\n{StackTrace}", Environment.StackTrace);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private string NavigateUp(string current, string path)
|
||||||
|
{
|
||||||
|
string[] pathParts = current.Split('/');
|
||||||
|
string[] navigateParts = path.Split('/');
|
||||||
|
int upCount = navigateParts.TakeWhile(part => part == "..").Count();
|
||||||
|
|
||||||
|
if (upCount >= pathParts.Length)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Cannot navigate up beyond the root");
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable<string> remainingCurrentPathParts = pathParts.Take(pathParts.Length - upCount);
|
||||||
|
IEnumerable<string> remainingNavigatePathParts = navigateParts.Skip(upCount);
|
||||||
|
|
||||||
|
return string.Join("/", remainingCurrentPathParts.Concat(remainingNavigatePathParts));
|
||||||
|
}
|
||||||
|
|
||||||
private void MainWindowServiceOnMainWindowOpened(object? sender, EventArgs e)
|
private void MainWindowServiceOnMainWindowOpened(object? sender, EventArgs e)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -65,5 +65,6 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<UpToDateCheckInput Remove="Screens\Workshop\Entries\Tabs\PluginListView.axaml" />
|
<UpToDateCheckInput Remove="Screens\Workshop\Entries\Tabs\PluginListView.axaml" />
|
||||||
<UpToDateCheckInput Remove="Screens\Workshop\Entries\Tabs\ProfileListView.axaml" />
|
<UpToDateCheckInput Remove="Screens\Workshop\Entries\Tabs\ProfileListView.axaml" />
|
||||||
|
<UpToDateCheckInput Remove="Screens\Workshop\Plugins\Dialogs\PluginDialogView.axaml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@ -30,6 +30,7 @@ namespace Artemis.UI.Routing
|
|||||||
new RouteRegistration<EntriesViewModel>("entries", [
|
new RouteRegistration<EntriesViewModel>("entries", [
|
||||||
new RouteRegistration<PluginListViewModel>("plugins", [
|
new RouteRegistration<PluginListViewModel>("plugins", [
|
||||||
new RouteRegistration<PluginDetailsViewModel>("details/{entryId:long}", [
|
new RouteRegistration<PluginDetailsViewModel>("details/{entryId:long}", [
|
||||||
|
new RouteRegistration<PluginManageViewModel>("manage"),
|
||||||
new RouteRegistration<EntryReleaseViewModel>("releases/{releaseId:long}")
|
new RouteRegistration<EntryReleaseViewModel>("releases/{releaseId:long}")
|
||||||
])
|
])
|
||||||
]),
|
]),
|
||||||
@ -40,6 +41,7 @@ namespace Artemis.UI.Routing
|
|||||||
]),
|
]),
|
||||||
new RouteRegistration<LayoutListViewModel>("layouts", [
|
new RouteRegistration<LayoutListViewModel>("layouts", [
|
||||||
new RouteRegistration<LayoutDetailsViewModel>("details/{entryId:long}", [
|
new RouteRegistration<LayoutDetailsViewModel>("details/{entryId:long}", [
|
||||||
|
new RouteRegistration<LayoutManageViewModel>("manage"),
|
||||||
new RouteRegistration<EntryReleaseViewModel>("releases/{releaseId:long}")
|
new RouteRegistration<EntryReleaseViewModel>("releases/{releaseId:long}")
|
||||||
])
|
])
|
||||||
])
|
])
|
||||||
|
|||||||
@ -32,7 +32,6 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</Panel>
|
</Panel>
|
||||||
|
|
||||||
|
|
||||||
<TextBlock Theme="{StaticResource TitleTextBlockStyle}"
|
<TextBlock Theme="{StaticResource TitleTextBlockStyle}"
|
||||||
MaxLines="3"
|
MaxLines="3"
|
||||||
TextTrimming="CharacterEllipsis"
|
TextTrimming="CharacterEllipsis"
|
||||||
@ -79,5 +78,9 @@
|
|||||||
<Run>Updated</Run>
|
<Run>Updated</Run>
|
||||||
<Run Text="{CompiledBinding UpdatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter='humanize'}"></Run>
|
<Run Text="{CompiledBinding UpdatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter='humanize'}"></Run>
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
|
|
||||||
|
<Button IsVisible="{CompiledBinding CanBeManaged}" Command="{CompiledBinding GoToManage}" Margin="0 10 0 0" HorizontalAlignment="Stretch">
|
||||||
|
Manage installation
|
||||||
|
</Button>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@ -1,10 +1,11 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.ReactiveUI;
|
||||||
|
|
||||||
namespace Artemis.UI.Screens.Workshop.Entries.Details;
|
namespace Artemis.UI.Screens.Workshop.Entries.Details;
|
||||||
|
|
||||||
public partial class EntryInfoView : UserControl
|
public partial class EntryInfoView : ReactiveUserControl<EntryInfoViewModel>
|
||||||
{
|
{
|
||||||
public EntryInfoView()
|
public EntryInfoView()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,29 +1,54 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reactive.Disposables;
|
||||||
|
using System.Reactive.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.UI.Shared;
|
using Artemis.UI.Shared;
|
||||||
|
using Artemis.UI.Shared.Routing;
|
||||||
using Artemis.UI.Shared.Services;
|
using Artemis.UI.Shared.Services;
|
||||||
using Artemis.WebClient.Workshop;
|
using Artemis.WebClient.Workshop;
|
||||||
|
using Artemis.WebClient.Workshop.Models;
|
||||||
|
using Artemis.WebClient.Workshop.Services;
|
||||||
|
using PropertyChanged.SourceGenerator;
|
||||||
|
using ReactiveUI;
|
||||||
|
|
||||||
namespace Artemis.UI.Screens.Workshop.Entries.Details;
|
namespace Artemis.UI.Screens.Workshop.Entries.Details;
|
||||||
|
|
||||||
public class EntryInfoViewModel : ViewModelBase
|
public partial class EntryInfoViewModel : ActivatableViewModelBase
|
||||||
{
|
{
|
||||||
|
private readonly IRouter _router;
|
||||||
private readonly INotificationService _notificationService;
|
private readonly INotificationService _notificationService;
|
||||||
public IEntryDetails Entry { get; }
|
[Notify] private bool _canBeManaged;
|
||||||
public DateTimeOffset? UpdatedAt { get; }
|
|
||||||
|
|
||||||
public EntryInfoViewModel(IEntryDetails entry, INotificationService notificationService)
|
public EntryInfoViewModel(IEntryDetails entry, IRouter router, INotificationService notificationService, IWorkshopService workshopService)
|
||||||
{
|
{
|
||||||
|
_router = router;
|
||||||
_notificationService = notificationService;
|
_notificationService = notificationService;
|
||||||
Entry = entry;
|
Entry = entry;
|
||||||
UpdatedAt = Entry.Releases.Any() ? Entry.Releases.Max(r => r.CreatedAt) : Entry.CreatedAt;
|
UpdatedAt = Entry.Releases.Any() ? Entry.Releases.Max(r => r.CreatedAt) : Entry.CreatedAt;
|
||||||
|
CanBeManaged = Entry.EntryType != EntryType.Profile && workshopService.GetInstalledEntry(entry.Id) != null;
|
||||||
|
|
||||||
|
this.WhenActivated(d =>
|
||||||
|
{
|
||||||
|
Observable.FromEventPattern<InstalledEntry>(x => workshopService.OnInstalledEntrySaved += x, x => workshopService.OnInstalledEntrySaved -= x)
|
||||||
|
.StartWith([])
|
||||||
|
.Subscribe(_ => CanBeManaged = Entry.EntryType != EntryType.Profile && workshopService.GetInstalledEntry(entry.Id) != null)
|
||||||
|
.DisposeWith(d);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IEntryDetails Entry { get; }
|
||||||
|
public DateTimeOffset? UpdatedAt { get; }
|
||||||
|
|
||||||
public async Task CopyShareLink()
|
public async Task CopyShareLink()
|
||||||
{
|
{
|
||||||
await Shared.UI.Clipboard.SetTextAsync($"{WorkshopConstants.WORKSHOP_URL}/entries/{Entry.Id}/{StringUtilities.UrlFriendly(Entry.Name)}");
|
await Shared.UI.Clipboard.SetTextAsync($"{WorkshopConstants.WORKSHOP_URL}/entries/{Entry.Id}/{StringUtilities.UrlFriendly(Entry.Name)}");
|
||||||
_notificationService.CreateNotification().WithTitle("Copied share link to clipboard.").Show();
|
_notificationService.CreateNotification().WithTitle("Copied share link to clipboard.").Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task GoToManage()
|
||||||
|
{
|
||||||
|
await _router.Navigate("/manage");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -79,11 +79,11 @@
|
|||||||
<!-- Install state -->
|
<!-- Install state -->
|
||||||
<StackPanel Grid.Column="2" Grid.Row="1" Margin="0 0 4 0" HorizontalAlignment="Right" VerticalAlignment="Bottom" IsVisible="{CompiledBinding IsInstalled}">
|
<StackPanel Grid.Column="2" Grid.Row="1" Margin="0 0 4 0" HorizontalAlignment="Right" VerticalAlignment="Bottom" IsVisible="{CompiledBinding IsInstalled}">
|
||||||
<TextBlock TextAlignment="Right" IsVisible="{CompiledBinding !UpdateAvailable}">
|
<TextBlock TextAlignment="Right" IsVisible="{CompiledBinding !UpdateAvailable}">
|
||||||
<avalonia:MaterialIcon Kind="CheckCircle" Foreground="{DynamicResource SystemAccentColorLight1}"/>
|
<avalonia:MaterialIcon Kind="CheckCircle" Foreground="{DynamicResource SystemAccentColorLight1}" Width="20" Height="20"/>
|
||||||
<Run>installed</Run>
|
<Run>installed</Run>
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
<TextBlock TextAlignment="Right" IsVisible="{CompiledBinding UpdateAvailable}">
|
<TextBlock TextAlignment="Right" IsVisible="{CompiledBinding UpdateAvailable}">
|
||||||
<avalonia:MaterialIcon Kind="Update" Foreground="{DynamicResource SystemAccentColorLight1}"/>
|
<avalonia:MaterialIcon Kind="Update" Foreground="{DynamicResource SystemAccentColorLight1}" Width="20" Height="20"/>
|
||||||
<Run>update available</Run>
|
<Run>update available</Run>
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|||||||
@ -0,0 +1,34 @@
|
|||||||
|
<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:entryReleases="clr-namespace:Artemis.UI.Screens.Workshop.EntryReleases"
|
||||||
|
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||||
|
xmlns:converters="clr-namespace:Artemis.UI.Converters"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Artemis.UI.Screens.Workshop.EntryReleases.EntryReleaseItemView"
|
||||||
|
x:DataType="entryReleases:EntryReleaseItemViewModel">
|
||||||
|
<UserControl.Resources>
|
||||||
|
<converters:DateTimeConverter x:Key="DateTimeConverter" />
|
||||||
|
</UserControl.Resources>
|
||||||
|
<UserControl.Styles>
|
||||||
|
<Style Selector="avalonia|MaterialIcon.status-icon">
|
||||||
|
<Setter Property="Width" Value="20" />
|
||||||
|
<Setter Property="Height" Value="20" />
|
||||||
|
<Setter Property="Background" Value="Transparent" />
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource SystemAccentColorLight1}" />
|
||||||
|
</Style>
|
||||||
|
</UserControl.Styles>
|
||||||
|
|
||||||
|
<Grid ColumnDefinitions="Auto,*" Margin="0 5">
|
||||||
|
<StackPanel Grid.Column="1" VerticalAlignment="Center">
|
||||||
|
<TextBlock Text="{CompiledBinding Release.Version}"></TextBlock>
|
||||||
|
<TextBlock Classes="subtitle" ToolTip.Tip="{CompiledBinding Release.CreatedAt, Converter={StaticResource DateTimeConverter}}">
|
||||||
|
<avalonia:MaterialIcon Kind="Calendar" />
|
||||||
|
<Run>Created</Run>
|
||||||
|
<Run Text="{CompiledBinding Release.CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter='humanize'}"></Run>
|
||||||
|
</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
<avalonia:MaterialIcon Classes="status-icon" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Right" Kind="CheckCircle" ToolTip.Tip="Current version" IsVisible="{CompiledBinding IsCurrentVersion}" />
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
using Avalonia.ReactiveUI;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Screens.Workshop.EntryReleases;
|
||||||
|
|
||||||
|
public partial class EntryReleaseItemView : ReactiveUserControl<EntryReleaseItemViewModel>
|
||||||
|
{
|
||||||
|
public EntryReleaseItemView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
using System;
|
||||||
|
using System.Reactive.Disposables;
|
||||||
|
using System.Reactive.Linq;
|
||||||
|
using Artemis.UI.Shared;
|
||||||
|
using Artemis.WebClient.Workshop;
|
||||||
|
using Artemis.WebClient.Workshop.Models;
|
||||||
|
using Artemis.WebClient.Workshop.Services;
|
||||||
|
using PropertyChanged.SourceGenerator;
|
||||||
|
using ReactiveUI;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Screens.Workshop.EntryReleases;
|
||||||
|
|
||||||
|
public partial class EntryReleaseItemViewModel : ActivatableViewModelBase
|
||||||
|
{
|
||||||
|
private readonly IWorkshopService _workshopService;
|
||||||
|
private readonly IEntryDetails _entry;
|
||||||
|
[Notify] private bool _isCurrentVersion;
|
||||||
|
|
||||||
|
public EntryReleaseItemViewModel(IWorkshopService workshopService, IEntryDetails entry, IRelease release)
|
||||||
|
{
|
||||||
|
_workshopService = workshopService;
|
||||||
|
_entry = entry;
|
||||||
|
|
||||||
|
Release = release;
|
||||||
|
UpdateIsCurrentVersion();
|
||||||
|
|
||||||
|
this.WhenActivated(d =>
|
||||||
|
{
|
||||||
|
Observable.FromEventPattern<InstalledEntry>(x => _workshopService.OnInstalledEntrySaved += x, x => _workshopService.OnInstalledEntrySaved -= x)
|
||||||
|
.Subscribe(_ => UpdateIsCurrentVersion())
|
||||||
|
.DisposeWith(d);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public IRelease Release { get; }
|
||||||
|
|
||||||
|
private void UpdateIsCurrentVersion()
|
||||||
|
{
|
||||||
|
IsCurrentVersion = _workshopService.GetInstalledEntry(_entry.Id)?.ReleaseId == Release.Id;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -46,7 +46,7 @@
|
|||||||
<avalonia:MaterialIcon Kind="ArrowBack" />
|
<avalonia:MaterialIcon Kind="ArrowBack" />
|
||||||
</Button>
|
</Button>
|
||||||
<TextBlock Grid.Row="0" Grid.Column="1" Classes="h4 no-margin">Release info</TextBlock>
|
<TextBlock Grid.Row="0" Grid.Column="1" Classes="h4 no-margin">Release info</TextBlock>
|
||||||
<Panel Grid.Column="2">
|
<StackPanel Grid.Column="2">
|
||||||
<!-- Install progress -->
|
<!-- Install progress -->
|
||||||
<StackPanel Orientation="Horizontal"
|
<StackPanel Orientation="Horizontal"
|
||||||
Spacing="5"
|
Spacing="5"
|
||||||
@ -66,14 +66,15 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<!-- Install button -->
|
<!-- Install button -->
|
||||||
<Button IsVisible="{CompiledBinding !InstallationInProgress}"
|
<Panel IsVisible="{CompiledBinding !InstallationInProgress}" HorizontalAlignment="Right">
|
||||||
HorizontalAlignment="Right"
|
<Button IsVisible="{CompiledBinding !IsCurrentVersion}" Classes="accent" Width="80" Command="{CompiledBinding Install}">
|
||||||
Classes="accent"
|
Install
|
||||||
Width="80"
|
</Button>
|
||||||
Command="{CompiledBinding Install}">
|
<Button IsVisible="{CompiledBinding IsCurrentVersion}" Classes="accent" Width="80" Command="{CompiledBinding Reinstall}">
|
||||||
Install
|
Re-install
|
||||||
</Button>
|
</Button>
|
||||||
</Panel>
|
</Panel>
|
||||||
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Border Classes="card-separator" />
|
<Border Classes="card-separator" />
|
||||||
<Grid Margin="-5 -10" ColumnDefinitions="*,*,*">
|
<Grid Margin="-5 -10" ColumnDefinitions="*,*,*">
|
||||||
|
|||||||
@ -8,6 +8,7 @@ using Artemis.UI.Shared.Services.Builders;
|
|||||||
using Artemis.UI.Shared.Utilities;
|
using Artemis.UI.Shared.Utilities;
|
||||||
using Artemis.WebClient.Workshop;
|
using Artemis.WebClient.Workshop;
|
||||||
using Artemis.WebClient.Workshop.Handlers.InstallationHandlers;
|
using Artemis.WebClient.Workshop.Handlers.InstallationHandlers;
|
||||||
|
using Artemis.WebClient.Workshop.Services;
|
||||||
using PropertyChanged.SourceGenerator;
|
using PropertyChanged.SourceGenerator;
|
||||||
using StrawberryShake;
|
using StrawberryShake;
|
||||||
|
|
||||||
@ -19,21 +20,25 @@ public partial class EntryReleaseViewModel : RoutableScreen<ReleaseDetailParamet
|
|||||||
private readonly IRouter _router;
|
private readonly IRouter _router;
|
||||||
private readonly INotificationService _notificationService;
|
private readonly INotificationService _notificationService;
|
||||||
private readonly IWindowService _windowService;
|
private readonly IWindowService _windowService;
|
||||||
|
private readonly IWorkshopService _workshopService;
|
||||||
private readonly EntryInstallationHandlerFactory _factory;
|
private readonly EntryInstallationHandlerFactory _factory;
|
||||||
private readonly Progress<StreamProgress> _progress = new();
|
private readonly Progress<StreamProgress> _progress = new();
|
||||||
|
|
||||||
[Notify] private IGetReleaseById_Release? _release;
|
[Notify] private IGetReleaseById_Release? _release;
|
||||||
[Notify] private float _installProgress;
|
[Notify] private float _installProgress;
|
||||||
[Notify] private bool _installationInProgress;
|
[Notify] private bool _installationInProgress;
|
||||||
|
[Notify] private bool _isCurrentVersion;
|
||||||
|
|
||||||
private CancellationTokenSource? _cts;
|
private CancellationTokenSource? _cts;
|
||||||
|
|
||||||
public EntryReleaseViewModel(IWorkshopClient client, IRouter router, INotificationService notificationService, IWindowService windowService, EntryInstallationHandlerFactory factory)
|
public EntryReleaseViewModel(IWorkshopClient client, IRouter router, INotificationService notificationService, IWindowService windowService, IWorkshopService workshopService,
|
||||||
|
EntryInstallationHandlerFactory factory)
|
||||||
{
|
{
|
||||||
_client = client;
|
_client = client;
|
||||||
_router = router;
|
_router = router;
|
||||||
_notificationService = notificationService;
|
_notificationService = notificationService;
|
||||||
_windowService = windowService;
|
_windowService = windowService;
|
||||||
|
_workshopService = workshopService;
|
||||||
_factory = factory;
|
_factory = factory;
|
||||||
_progress.ProgressChanged += (_, f) => InstallProgress = f.ProgressPercentage;
|
_progress.ProgressChanged += (_, f) => InstallProgress = f.ProgressPercentage;
|
||||||
}
|
}
|
||||||
@ -56,18 +61,32 @@ public partial class EntryReleaseViewModel : RoutableScreen<ReleaseDetailParamet
|
|||||||
IEntryInstallationHandler handler = _factory.CreateHandler(Release.Entry.EntryType);
|
IEntryInstallationHandler handler = _factory.CreateHandler(Release.Entry.EntryType);
|
||||||
EntryInstallResult result = await handler.InstallAsync(Release.Entry, Release, _progress, _cts.Token);
|
EntryInstallResult result = await handler.InstallAsync(Release.Entry, Release, _progress, _cts.Token);
|
||||||
if (result.IsSuccess)
|
if (result.IsSuccess)
|
||||||
|
{
|
||||||
_notificationService.CreateNotification().WithTitle("Installation succeeded").WithSeverity(NotificationSeverity.Success).Show();
|
_notificationService.CreateNotification().WithTitle("Installation succeeded").WithSeverity(NotificationSeverity.Success).Show();
|
||||||
|
IsCurrentVersion = true;
|
||||||
|
InstallationInProgress = false;
|
||||||
|
await Manage();
|
||||||
|
}
|
||||||
else if (!_cts.IsCancellationRequested)
|
else if (!_cts.IsCancellationRequested)
|
||||||
_notificationService.CreateNotification().WithTitle("Installation failed").WithMessage(result.Message).WithSeverity(NotificationSeverity.Error).Show();
|
_notificationService.CreateNotification().WithTitle("Installation failed").WithMessage(result.Message).WithSeverity(NotificationSeverity.Error).Show();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
InstallationInProgress = false;
|
||||||
_windowService.ShowExceptionDialog("Failed to install workshop entry", e);
|
_windowService.ShowExceptionDialog("Failed to install workshop entry", e);
|
||||||
}
|
}
|
||||||
finally
|
}
|
||||||
{
|
|
||||||
InstallationInProgress = false;
|
public async Task Manage()
|
||||||
}
|
{
|
||||||
|
if (Release?.Entry.EntryType != EntryType.Profile)
|
||||||
|
await _router.Navigate("../../manage");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Reinstall()
|
||||||
|
{
|
||||||
|
if (await _windowService.ShowConfirmContentDialog("Reinstall entry", "Are you sure you want to reinstall this entry?"))
|
||||||
|
await Install();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Cancel()
|
public void Cancel()
|
||||||
@ -80,6 +99,7 @@ public partial class EntryReleaseViewModel : RoutableScreen<ReleaseDetailParamet
|
|||||||
{
|
{
|
||||||
IOperationResult<IGetReleaseByIdResult> result = await _client.GetReleaseById.ExecuteAsync(parameters.ReleaseId, cancellationToken);
|
IOperationResult<IGetReleaseByIdResult> result = await _client.GetReleaseById.ExecuteAsync(parameters.ReleaseId, cancellationToken);
|
||||||
Release = result.Data?.Release;
|
Release = result.Data?.Release;
|
||||||
|
IsCurrentVersion = Release != null && _workshopService.GetInstalledEntry(Release.Entry.Id)?.ReleaseId == Release.Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Overrides of RoutableScreen
|
#region Overrides of RoutableScreen
|
||||||
|
|||||||
@ -2,38 +2,12 @@
|
|||||||
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"
|
||||||
xmlns:converters="clr-namespace:Artemis.UI.Converters"
|
|
||||||
xmlns:sharedConverters="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared"
|
|
||||||
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
|
||||||
xmlns:workshop="clr-namespace:Artemis.WebClient.Workshop;assembly=Artemis.WebClient.Workshop"
|
|
||||||
xmlns:entryReleases="clr-namespace:Artemis.UI.Screens.Workshop.EntryReleases"
|
xmlns:entryReleases="clr-namespace:Artemis.UI.Screens.Workshop.EntryReleases"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Artemis.UI.Screens.Workshop.EntryReleases.EntryReleasesView"
|
x:Class="Artemis.UI.Screens.Workshop.EntryReleases.EntryReleasesView"
|
||||||
x:DataType="entryReleases:EntryReleasesViewModel">
|
x:DataType="entryReleases:EntryReleasesViewModel"><StackPanel>
|
||||||
<UserControl.Resources>
|
|
||||||
<converters:DateTimeConverter x:Key="DateTimeConverter" />
|
|
||||||
<sharedConverters:BytesToStringConverter x:Key="BytesToStringConverter" />
|
|
||||||
</UserControl.Resources>
|
|
||||||
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}">Releases</TextBlock>
|
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}">Releases</TextBlock>
|
||||||
<Border Classes="card-separator" />
|
<Border Classes="card-separator" />
|
||||||
|
<ListBox ItemsSource="{CompiledBinding Releases}" SelectedItem="{CompiledBinding SelectedRelease}"/>
|
||||||
<ListBox ItemsSource="{CompiledBinding Releases}" SelectedItem="{CompiledBinding SelectedRelease}" Classes="release-list">
|
|
||||||
<ListBox.ItemTemplate>
|
|
||||||
<DataTemplate x:DataType="workshop:IRelease">
|
|
||||||
<Grid ColumnDefinitions="Auto,*" Margin="0 5">
|
|
||||||
<StackPanel Grid.Column="1" VerticalAlignment="Center">
|
|
||||||
<TextBlock Text="{CompiledBinding Version}"></TextBlock>
|
|
||||||
<TextBlock Classes="subtitle" ToolTip.Tip="{CompiledBinding CreatedAt, Converter={StaticResource DateTimeConverter}}">
|
|
||||||
<avalonia:MaterialIcon Kind="Calendar" />
|
|
||||||
<Run>Created</Run>
|
|
||||||
<Run Text="{CompiledBinding CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter='humanize'}"></Run>
|
|
||||||
</TextBlock>
|
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
|
||||||
</DataTemplate>
|
|
||||||
</ListBox.ItemTemplate>
|
|
||||||
</ListBox>
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@ -16,38 +16,34 @@ namespace Artemis.UI.Screens.Workshop.EntryReleases;
|
|||||||
public partial class EntryReleasesViewModel : ActivatableViewModelBase
|
public partial class EntryReleasesViewModel : ActivatableViewModelBase
|
||||||
{
|
{
|
||||||
private readonly IRouter _router;
|
private readonly IRouter _router;
|
||||||
[Notify] private IRelease? _selectedRelease;
|
[Notify] private EntryReleaseItemViewModel? _selectedRelease;
|
||||||
|
|
||||||
public EntryReleasesViewModel(IEntryDetails entry, IRouter router)
|
public EntryReleasesViewModel(IEntryDetails entry, IRouter router, Func<IRelease, EntryReleaseItemViewModel> getEntryReleaseItemViewModel)
|
||||||
{
|
{
|
||||||
_router = router;
|
_router = router;
|
||||||
|
|
||||||
Entry = entry;
|
Entry = entry;
|
||||||
Releases = Entry.Releases.OrderByDescending(r => r.CreatedAt).Take(5).Cast<IRelease>().ToList();
|
Releases = Entry.Releases.OrderByDescending(r => r.CreatedAt).Take(5).Select(r => getEntryReleaseItemViewModel(r)).ToList();
|
||||||
NavigateToRelease = ReactiveCommand.CreateFromTask<IRelease>(ExecuteNavigateToRelease);
|
NavigateToRelease = ReactiveCommand.CreateFromTask<IRelease>(ExecuteNavigateToRelease);
|
||||||
|
|
||||||
this.WhenActivated(d =>
|
this.WhenActivated(d =>
|
||||||
{
|
{
|
||||||
router.CurrentPath.Subscribe(p => SelectedRelease = p != null && p.Contains("releases") && float.TryParse(p.Split('/').Last(), out float releaseId)
|
router.CurrentPath.Subscribe(p => SelectedRelease = p != null && p.Contains("releases") && float.TryParse(p.Split('/').Last(), out float releaseId)
|
||||||
? Releases.FirstOrDefault(r => r.Id == releaseId)
|
? Releases.FirstOrDefault(r => r.Release.Id == releaseId)
|
||||||
: null)
|
: null)
|
||||||
.DisposeWith(d);
|
.DisposeWith(d);
|
||||||
|
|
||||||
this.WhenAnyValue(vm => vm.SelectedRelease)
|
this.WhenAnyValue(vm => vm.SelectedRelease)
|
||||||
.WhereNotNull()
|
.WhereNotNull()
|
||||||
.Subscribe(s => ExecuteNavigateToRelease(s))
|
.Subscribe(s => ExecuteNavigateToRelease(s.Release))
|
||||||
.DisposeWith(d);
|
.DisposeWith(d);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEntryDetails Entry { get; }
|
public IEntryDetails Entry { get; }
|
||||||
public List<IRelease> Releases { get; }
|
public List<EntryReleaseItemViewModel> Releases { get; }
|
||||||
|
|
||||||
public ReactiveCommand<IRelease, Unit> NavigateToRelease { get; }
|
public ReactiveCommand<IRelease, Unit> NavigateToRelease { get; }
|
||||||
|
|
||||||
public Func<IEntryDetails, IRelease, Task<bool>> OnInstallationStarted { get; set; }
|
|
||||||
public Func<InstalledEntry, Task>? OnInstallationFinished { get; set; }
|
|
||||||
|
|
||||||
private async Task ExecuteNavigateToRelease(IRelease release)
|
private async Task ExecuteNavigateToRelease(IRelease release)
|
||||||
{
|
{
|
||||||
switch (Entry.EntryType)
|
switch (Entry.EntryType)
|
||||||
|
|||||||
@ -1,20 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Artemis.Core;
|
|
||||||
using Artemis.Core.Services;
|
|
||||||
using Artemis.UI.Screens.Workshop.Entries.Details;
|
using Artemis.UI.Screens.Workshop.Entries.Details;
|
||||||
using Artemis.UI.Screens.Workshop.EntryReleases;
|
using Artemis.UI.Screens.Workshop.EntryReleases;
|
||||||
using Artemis.UI.Screens.Workshop.Layout.Dialogs;
|
|
||||||
using Artemis.UI.Screens.Workshop.Parameters;
|
using Artemis.UI.Screens.Workshop.Parameters;
|
||||||
using Artemis.UI.Shared.Routing;
|
using Artemis.UI.Shared.Routing;
|
||||||
using Artemis.UI.Shared.Services;
|
|
||||||
using Artemis.WebClient.Workshop;
|
using Artemis.WebClient.Workshop;
|
||||||
using Artemis.WebClient.Workshop.Models;
|
|
||||||
using Artemis.WebClient.Workshop.Services;
|
|
||||||
using PropertyChanged.SourceGenerator;
|
using PropertyChanged.SourceGenerator;
|
||||||
using StrawberryShake;
|
using StrawberryShake;
|
||||||
|
|
||||||
@ -23,8 +14,6 @@ namespace Artemis.UI.Screens.Workshop.Layout;
|
|||||||
public partial class LayoutDetailsViewModel : RoutableHostScreen<RoutableScreen, WorkshopDetailParameters>
|
public partial class LayoutDetailsViewModel : RoutableHostScreen<RoutableScreen, WorkshopDetailParameters>
|
||||||
{
|
{
|
||||||
private readonly IWorkshopClient _client;
|
private readonly IWorkshopClient _client;
|
||||||
private readonly IDeviceService _deviceService;
|
|
||||||
private readonly IWindowService _windowService;
|
|
||||||
private readonly Func<IEntryDetails, EntryInfoViewModel> _getEntryInfoViewModel;
|
private readonly Func<IEntryDetails, EntryInfoViewModel> _getEntryInfoViewModel;
|
||||||
private readonly Func<IEntryDetails, EntryReleasesViewModel> _getEntryReleasesViewModel;
|
private readonly Func<IEntryDetails, EntryReleasesViewModel> _getEntryReleasesViewModel;
|
||||||
private readonly Func<IEntryDetails, EntryImagesViewModel> _getEntryImagesViewModel;
|
private readonly Func<IEntryDetails, EntryImagesViewModel> _getEntryImagesViewModel;
|
||||||
@ -34,16 +23,12 @@ public partial class LayoutDetailsViewModel : RoutableHostScreen<RoutableScreen,
|
|||||||
[Notify] private EntryImagesViewModel? _entryImagesViewModel;
|
[Notify] private EntryImagesViewModel? _entryImagesViewModel;
|
||||||
|
|
||||||
public LayoutDetailsViewModel(IWorkshopClient client,
|
public LayoutDetailsViewModel(IWorkshopClient client,
|
||||||
IDeviceService deviceService,
|
|
||||||
IWindowService windowService,
|
|
||||||
LayoutDescriptionViewModel layoutDescriptionViewModel,
|
LayoutDescriptionViewModel layoutDescriptionViewModel,
|
||||||
Func<IEntryDetails, EntryInfoViewModel> getEntryInfoViewModel,
|
Func<IEntryDetails, EntryInfoViewModel> getEntryInfoViewModel,
|
||||||
Func<IEntryDetails, EntryReleasesViewModel> getEntryReleasesViewModel,
|
Func<IEntryDetails, EntryReleasesViewModel> getEntryReleasesViewModel,
|
||||||
Func<IEntryDetails, EntryImagesViewModel> getEntryImagesViewModel)
|
Func<IEntryDetails, EntryImagesViewModel> getEntryImagesViewModel)
|
||||||
{
|
{
|
||||||
_client = client;
|
_client = client;
|
||||||
_deviceService = deviceService;
|
|
||||||
_windowService = windowService;
|
|
||||||
_getEntryInfoViewModel = getEntryInfoViewModel;
|
_getEntryInfoViewModel = getEntryInfoViewModel;
|
||||||
_getEntryReleasesViewModel = getEntryReleasesViewModel;
|
_getEntryReleasesViewModel = getEntryReleasesViewModel;
|
||||||
_getEntryImagesViewModel = getEntryImagesViewModel;
|
_getEntryImagesViewModel = getEntryImagesViewModel;
|
||||||
@ -70,28 +55,6 @@ public partial class LayoutDetailsViewModel : RoutableHostScreen<RoutableScreen,
|
|||||||
EntryInfoViewModel = Entry != null ? _getEntryInfoViewModel(Entry) : null;
|
EntryInfoViewModel = Entry != null ? _getEntryInfoViewModel(Entry) : null;
|
||||||
EntryReleasesViewModel = Entry != null ? _getEntryReleasesViewModel(Entry) : null;
|
EntryReleasesViewModel = Entry != null ? _getEntryReleasesViewModel(Entry) : null;
|
||||||
EntryImagesViewModel = Entry != null ? _getEntryImagesViewModel(Entry) : null;
|
EntryImagesViewModel = Entry != null ? _getEntryImagesViewModel(Entry) : null;
|
||||||
|
|
||||||
if (EntryReleasesViewModel != null)
|
|
||||||
EntryReleasesViewModel.OnInstallationFinished = OnInstallationFinished;
|
|
||||||
|
|
||||||
LayoutDescriptionViewModel.Entry = Entry;
|
LayoutDescriptionViewModel.Entry = Entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OnInstallationFinished(InstalledEntry installedEntry)
|
|
||||||
{
|
|
||||||
// Find compatible devices
|
|
||||||
ArtemisLayout layout = new(Path.Combine(installedEntry.GetReleaseDirectory().FullName, "layout.xml"));
|
|
||||||
List<ArtemisDevice> devices = _deviceService.Devices.Where(d => d.RgbDevice.DeviceInfo.DeviceType == layout.RgbLayout.Type).ToList();
|
|
||||||
|
|
||||||
// If any are found, offer to apply
|
|
||||||
if (devices.Any())
|
|
||||||
{
|
|
||||||
await _windowService.CreateContentDialog()
|
|
||||||
.WithTitle("Apply layout to devices")
|
|
||||||
.WithViewModel(out DeviceSelectionDialogViewModel vm, devices, installedEntry)
|
|
||||||
.WithCloseButtonText(null)
|
|
||||||
.HavingPrimaryButton(b => b.WithText("Continue").WithCommand(vm.Apply))
|
|
||||||
.ShowAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
<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:layout="clr-namespace:Artemis.UI.Screens.Workshop.Layout"
|
||||||
|
xmlns:surfaceEditor="clr-namespace:Artemis.UI.Screens.SurfaceEditor"
|
||||||
|
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Artemis.UI.Screens.Workshop.Layout.LayoutManageView"
|
||||||
|
x:DataType="layout:LayoutManageViewModel">
|
||||||
|
<Border Classes="card" VerticalAlignment="Top">
|
||||||
|
<StackPanel>
|
||||||
|
<Grid ColumnDefinitions="Auto,*,Auto">
|
||||||
|
<Button Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Classes="icon-button" Command="{CompiledBinding Close}">
|
||||||
|
<avalonia:MaterialIcon Kind="ArrowBack" />
|
||||||
|
</Button>
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="1" Classes="h4 no-margin">Manage layout</TextBlock>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Border Classes="card-separator" />
|
||||||
|
|
||||||
|
<TextBlock IsVisible="{CompiledBinding !Devices.Count}">
|
||||||
|
This layout is made for devices of type
|
||||||
|
<Run FontWeight="Bold" Text="{CompiledBinding Layout.RgbLayout.Type}"/>.<LineBreak/>
|
||||||
|
Unfortunately, none were detected.
|
||||||
|
</TextBlock>
|
||||||
|
<StackPanel IsVisible="{CompiledBinding Devices.Count}">
|
||||||
|
<TextBlock>
|
||||||
|
Select the devices on which you would like to apply the downloaded layout.
|
||||||
|
</TextBlock>
|
||||||
|
<ItemsControl Name="EffectDescriptorsList" ItemsSource="{CompiledBinding Devices}" Margin="0 10">
|
||||||
|
<ItemsControl.DataTemplates>
|
||||||
|
<DataTemplate DataType="{x:Type surfaceEditor:ListDeviceViewModel}">
|
||||||
|
<CheckBox IsChecked="{CompiledBinding IsSelected}">
|
||||||
|
<TextBlock Text="{CompiledBinding Device.RgbDevice.DeviceInfo.DeviceName}"></TextBlock>
|
||||||
|
</CheckBox>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.DataTemplates>
|
||||||
|
</ItemsControl>
|
||||||
|
|
||||||
|
<Button Command="{CompiledBinding Apply}">Apply</Button>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</UserControl>
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.ReactiveUI;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Screens.Workshop.Layout;
|
||||||
|
|
||||||
|
public partial class LayoutManageView : ReactiveUserControl<LayoutManageViewModel>
|
||||||
|
{
|
||||||
|
public LayoutManageView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
103
src/Artemis.UI/Screens/Workshop/Layout/LayoutManageViewModel.cs
Normal file
103
src/Artemis.UI/Screens/Workshop/Layout/LayoutManageViewModel.cs
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reactive;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Artemis.Core;
|
||||||
|
using Artemis.Core.Services;
|
||||||
|
using Artemis.UI.DryIoc.Factories;
|
||||||
|
using Artemis.UI.Screens.SurfaceEditor;
|
||||||
|
using Artemis.UI.Screens.Workshop.Parameters;
|
||||||
|
using Artemis.UI.Shared.Routing;
|
||||||
|
using Artemis.UI.Shared.Services;
|
||||||
|
using Artemis.WebClient.Workshop.Models;
|
||||||
|
using Artemis.WebClient.Workshop.Providers;
|
||||||
|
using Artemis.WebClient.Workshop.Services;
|
||||||
|
using Avalonia.Threading;
|
||||||
|
using PropertyChanged.SourceGenerator;
|
||||||
|
using ReactiveUI;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Screens.Workshop.Layout;
|
||||||
|
|
||||||
|
public partial class LayoutManageViewModel : RoutableScreen<WorkshopDetailParameters>
|
||||||
|
{
|
||||||
|
private readonly ISurfaceVmFactory _surfaceVmFactory;
|
||||||
|
private readonly IRouter _router;
|
||||||
|
private readonly IWorkshopService _workshopService;
|
||||||
|
private readonly IDeviceService _deviceService;
|
||||||
|
private readonly WorkshopLayoutProvider _layoutProvider;
|
||||||
|
private readonly IWindowService _windowService;
|
||||||
|
[Notify] private ArtemisLayout? _layout;
|
||||||
|
[Notify] private InstalledEntry? _entry;
|
||||||
|
[Notify] private ObservableCollection<ListDeviceViewModel>? _devices;
|
||||||
|
|
||||||
|
public LayoutManageViewModel(ISurfaceVmFactory surfaceVmFactory,
|
||||||
|
IRouter router,
|
||||||
|
IWorkshopService workshopService,
|
||||||
|
IDeviceService deviceService,
|
||||||
|
WorkshopLayoutProvider layoutProvider,
|
||||||
|
IWindowService windowService)
|
||||||
|
{
|
||||||
|
_surfaceVmFactory = surfaceVmFactory;
|
||||||
|
_router = router;
|
||||||
|
_workshopService = workshopService;
|
||||||
|
_deviceService = deviceService;
|
||||||
|
_layoutProvider = layoutProvider;
|
||||||
|
_windowService = windowService;
|
||||||
|
Apply = ReactiveCommand.Create(ExecuteApply);
|
||||||
|
ParameterSource = ParameterSource.Route;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReactiveCommand<Unit, Unit> Apply { get; }
|
||||||
|
|
||||||
|
public async Task Close()
|
||||||
|
{
|
||||||
|
await _router.GoUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task OnNavigating(WorkshopDetailParameters parameters, NavigationArguments args, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
InstalledEntry? installedEntry = _workshopService.GetInstalledEntry(parameters.EntryId);
|
||||||
|
if (installedEntry == null)
|
||||||
|
{
|
||||||
|
// TODO: Fix cancelling without this workaround, currently navigation is stopped but the page still opens
|
||||||
|
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||||
|
{
|
||||||
|
await _windowService.ShowConfirmContentDialog("Entry not found", "The entry you're trying to manage could not be found.", "Go back", null);
|
||||||
|
await Close();
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Layout = new ArtemisLayout(Path.Combine(installedEntry.GetReleaseDirectory().FullName, "layout.xml"));
|
||||||
|
if (!Layout.IsValid)
|
||||||
|
{
|
||||||
|
// TODO: Fix cancelling without this workaround, currently navigation is stopped but the page still opens
|
||||||
|
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||||
|
{
|
||||||
|
await _windowService.ShowConfirmContentDialog("Invalid layout", "The layout of the entry you're trying to manage is invalid.", "Go back", null);
|
||||||
|
await Close();
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry = installedEntry;
|
||||||
|
Devices = new ObservableCollection<ListDeviceViewModel>(_deviceService.Devices
|
||||||
|
.Where(d => d.RgbDevice.DeviceInfo.DeviceType == Layout.RgbLayout.Type)
|
||||||
|
.Select(_surfaceVmFactory.ListDeviceViewModel));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExecuteApply()
|
||||||
|
{
|
||||||
|
if (Devices == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (ListDeviceViewModel listDeviceViewModel in Devices.Where(d => d.IsSelected))
|
||||||
|
{
|
||||||
|
_layoutProvider.ConfigureDevice(listDeviceViewModel.Device, Entry);
|
||||||
|
_deviceService.SaveDevice(listDeviceViewModel.Device);
|
||||||
|
_deviceService.LoadDeviceLayout(listDeviceViewModel.Device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,19 +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:dialogs="clr-namespace:Artemis.UI.Screens.Workshop.Plugins.Dialogs"
|
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
|
||||||
x:Class="Artemis.UI.Screens.Workshop.Plugins.Dialogs.PluginDialogView"
|
|
||||||
x:DataType="dialogs:PluginDialogViewModel">
|
|
||||||
<Grid ColumnDefinitions="4*,5*" Width="800" Height="160">
|
|
||||||
<ContentControl Grid.Column="0" Content="{CompiledBinding PluginViewModel}" />
|
|
||||||
|
|
||||||
<Border Grid.Column="1" BorderBrush="{DynamicResource ButtonBorderBrush}" BorderThickness="1 0 0 0" Margin="10 0 0 0" Padding="10 0 0 0">
|
|
||||||
<Grid RowDefinitions="Auto,*">
|
|
||||||
<TextBlock Classes="h5">Plugin features</TextBlock>
|
|
||||||
<ListBox Grid.Row="1" MaxHeight="135" ItemsSource="{CompiledBinding PluginFeatures}" />
|
|
||||||
</Grid>
|
|
||||||
</Border>
|
|
||||||
</Grid>
|
|
||||||
</UserControl>
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
using Avalonia.ReactiveUI;
|
|
||||||
|
|
||||||
namespace Artemis.UI.Screens.Workshop.Plugins.Dialogs;
|
|
||||||
|
|
||||||
public partial class PluginDialogView : ReactiveUserControl<PluginDialogViewModel>
|
|
||||||
{
|
|
||||||
public PluginDialogView()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reactive.Linq;
|
|
||||||
using Artemis.Core;
|
|
||||||
using Artemis.UI.DryIoc.Factories;
|
|
||||||
using Artemis.UI.Screens.Plugins;
|
|
||||||
using Artemis.UI.Screens.Plugins.Features;
|
|
||||||
using Artemis.UI.Shared;
|
|
||||||
using ReactiveUI;
|
|
||||||
|
|
||||||
namespace Artemis.UI.Screens.Workshop.Plugins.Dialogs;
|
|
||||||
|
|
||||||
public class PluginDialogViewModel : ContentDialogViewModelBase
|
|
||||||
{
|
|
||||||
public PluginDialogViewModel(Plugin plugin, ISettingsVmFactory settingsVmFactory)
|
|
||||||
{
|
|
||||||
PluginViewModel = settingsVmFactory.PluginViewModel(plugin, ReactiveCommand.Create(() => {}, Observable.Empty<bool>()));
|
|
||||||
PluginFeatures = new ObservableCollection<PluginFeatureViewModel>(plugin.Features.Select(f => settingsVmFactory.PluginFeatureViewModel(f, false)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public PluginViewModel PluginViewModel { get; }
|
|
||||||
public ObservableCollection<PluginFeatureViewModel> PluginFeatures { get; }
|
|
||||||
}
|
|
||||||
@ -1,20 +1,13 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Artemis.Core;
|
|
||||||
using Artemis.Core.Services;
|
|
||||||
using Artemis.UI.Screens.Workshop.Entries.Details;
|
using Artemis.UI.Screens.Workshop.Entries.Details;
|
||||||
using Artemis.UI.Screens.Workshop.Entries.List;
|
using Artemis.UI.Screens.Workshop.Entries.List;
|
||||||
using Artemis.UI.Screens.Workshop.EntryReleases;
|
using Artemis.UI.Screens.Workshop.EntryReleases;
|
||||||
using Artemis.UI.Screens.Workshop.Parameters;
|
using Artemis.UI.Screens.Workshop.Parameters;
|
||||||
using Artemis.UI.Screens.Workshop.Plugins.Dialogs;
|
|
||||||
using Artemis.UI.Shared.Routing;
|
using Artemis.UI.Shared.Routing;
|
||||||
using Artemis.UI.Shared.Services;
|
|
||||||
using Artemis.WebClient.Workshop;
|
using Artemis.WebClient.Workshop;
|
||||||
using Artemis.WebClient.Workshop.Models;
|
|
||||||
using PropertyChanged.SourceGenerator;
|
using PropertyChanged.SourceGenerator;
|
||||||
using StrawberryShake;
|
using StrawberryShake;
|
||||||
|
|
||||||
@ -23,8 +16,6 @@ namespace Artemis.UI.Screens.Workshop.Plugins;
|
|||||||
public partial class PluginDetailsViewModel : RoutableHostScreen<RoutableScreen, WorkshopDetailParameters>
|
public partial class PluginDetailsViewModel : RoutableHostScreen<RoutableScreen, WorkshopDetailParameters>
|
||||||
{
|
{
|
||||||
private readonly IWorkshopClient _client;
|
private readonly IWorkshopClient _client;
|
||||||
private readonly IWindowService _windowService;
|
|
||||||
private readonly IPluginManagementService _pluginManagementService;
|
|
||||||
private readonly Func<IEntryDetails, EntryInfoViewModel> _getEntryInfoViewModel;
|
private readonly Func<IEntryDetails, EntryInfoViewModel> _getEntryInfoViewModel;
|
||||||
private readonly Func<IEntryDetails, EntryReleasesViewModel> _getEntryReleasesViewModel;
|
private readonly Func<IEntryDetails, EntryReleasesViewModel> _getEntryReleasesViewModel;
|
||||||
private readonly Func<IEntryDetails, EntryImagesViewModel> _getEntryImagesViewModel;
|
private readonly Func<IEntryDetails, EntryImagesViewModel> _getEntryImagesViewModel;
|
||||||
@ -35,16 +26,12 @@ public partial class PluginDetailsViewModel : RoutableHostScreen<RoutableScreen,
|
|||||||
[Notify] private ReadOnlyObservableCollection<EntryListItemViewModel>? _dependants;
|
[Notify] private ReadOnlyObservableCollection<EntryListItemViewModel>? _dependants;
|
||||||
|
|
||||||
public PluginDetailsViewModel(IWorkshopClient client,
|
public PluginDetailsViewModel(IWorkshopClient client,
|
||||||
IWindowService windowService,
|
|
||||||
IPluginManagementService pluginManagementService,
|
|
||||||
PluginDescriptionViewModel pluginDescriptionViewModel,
|
PluginDescriptionViewModel pluginDescriptionViewModel,
|
||||||
Func<IEntryDetails, EntryInfoViewModel> getEntryInfoViewModel,
|
Func<IEntryDetails, EntryInfoViewModel> getEntryInfoViewModel,
|
||||||
Func<IEntryDetails, EntryReleasesViewModel> getEntryReleasesViewModel,
|
Func<IEntryDetails, EntryReleasesViewModel> getEntryReleasesViewModel,
|
||||||
Func<IEntryDetails, EntryImagesViewModel> getEntryImagesViewModel)
|
Func<IEntryDetails, EntryImagesViewModel> getEntryImagesViewModel)
|
||||||
{
|
{
|
||||||
_client = client;
|
_client = client;
|
||||||
_windowService = windowService;
|
|
||||||
_pluginManagementService = pluginManagementService;
|
|
||||||
_getEntryInfoViewModel = getEntryInfoViewModel;
|
_getEntryInfoViewModel = getEntryInfoViewModel;
|
||||||
_getEntryReleasesViewModel = getEntryReleasesViewModel;
|
_getEntryReleasesViewModel = getEntryReleasesViewModel;
|
||||||
_getEntryImagesViewModel = getEntryImagesViewModel;
|
_getEntryImagesViewModel = getEntryImagesViewModel;
|
||||||
@ -72,35 +59,6 @@ public partial class PluginDetailsViewModel : RoutableHostScreen<RoutableScreen,
|
|||||||
EntryReleasesViewModel = Entry != null ? _getEntryReleasesViewModel(Entry) : null;
|
EntryReleasesViewModel = Entry != null ? _getEntryReleasesViewModel(Entry) : null;
|
||||||
EntryImagesViewModel = Entry != null ? _getEntryImagesViewModel(Entry) : null;
|
EntryImagesViewModel = Entry != null ? _getEntryImagesViewModel(Entry) : null;
|
||||||
|
|
||||||
if (EntryReleasesViewModel != null)
|
|
||||||
{
|
|
||||||
EntryReleasesViewModel.OnInstallationStarted = OnInstallationStarted;
|
|
||||||
EntryReleasesViewModel.OnInstallationFinished = OnInstallationFinished;
|
|
||||||
}
|
|
||||||
|
|
||||||
await PluginDescriptionViewModel.SetEntry(Entry, cancellationToken);
|
await PluginDescriptionViewModel.SetEntry(Entry, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> OnInstallationStarted(IEntryDetails entryDetails, IRelease release)
|
|
||||||
{
|
|
||||||
bool confirm = await _windowService.ShowConfirmContentDialog(
|
|
||||||
"Installing plugin",
|
|
||||||
$"You are about to install version {release.Version} of {entryDetails.Name}. \r\n\r\n" +
|
|
||||||
"Plugins are NOT verified by Artemis and could harm your PC, if you have doubts about a plugin please ask on Discord!",
|
|
||||||
"I trust this plugin, install it"
|
|
||||||
);
|
|
||||||
|
|
||||||
return !confirm;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task OnInstallationFinished(InstalledEntry installedEntry)
|
|
||||||
{
|
|
||||||
if (!installedEntry.TryGetMetadata("PluginId", out Guid pluginId))
|
|
||||||
return;
|
|
||||||
Plugin? plugin = _pluginManagementService.GetAllPlugins().FirstOrDefault(p => p.Guid == pluginId);
|
|
||||||
if (plugin == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
await _windowService.CreateContentDialog().WithTitle("Manage plugin").WithViewModel(out PluginDialogViewModel _, plugin).WithFullScreen().ShowAsync();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
<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:plugins="clr-namespace:Artemis.UI.Screens.Workshop.Plugins"
|
||||||
|
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Artemis.UI.Screens.Workshop.Plugins.PluginManageView"
|
||||||
|
x:DataType="plugins:PluginManageViewModel">
|
||||||
|
<Border Classes="card" VerticalAlignment="Top">
|
||||||
|
<StackPanel>
|
||||||
|
<Grid ColumnDefinitions="Auto,*,Auto">
|
||||||
|
<Button Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Classes="icon-button" Command="{CompiledBinding Close}">
|
||||||
|
<avalonia:MaterialIcon Kind="ArrowBack" />
|
||||||
|
</Button>
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="1" Classes="h4 no-margin">Manage plugin</TextBlock>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Border Classes="card-separator" />
|
||||||
|
|
||||||
|
<Grid ColumnDefinitions="4*,5*" Height="160">
|
||||||
|
<ContentControl Grid.Column="0" Content="{CompiledBinding PluginViewModel}" />
|
||||||
|
|
||||||
|
<Border Grid.Column="1" BorderBrush="{DynamicResource ButtonBorderBrush}" BorderThickness="1 0 0 0" Margin="10 0 0 0" Padding="10 0 0 0">
|
||||||
|
<Grid RowDefinitions="Auto,*">
|
||||||
|
<TextBlock Classes="h5">Plugin features</TextBlock>
|
||||||
|
<ListBox Grid.Row="1" MaxHeight="135" ItemsSource="{CompiledBinding PluginFeatures}" />
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</UserControl>
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.ReactiveUI;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Screens.Workshop.Plugins;
|
||||||
|
|
||||||
|
public partial class PluginManageView : ReactiveUserControl<PluginManageViewModel>
|
||||||
|
{
|
||||||
|
public PluginManageView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,77 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Artemis.Core;
|
||||||
|
using Artemis.Core.Services;
|
||||||
|
using Artemis.UI.DryIoc.Factories;
|
||||||
|
using Artemis.UI.Screens.Plugins;
|
||||||
|
using Artemis.UI.Screens.Plugins.Features;
|
||||||
|
using Artemis.UI.Screens.Workshop.Parameters;
|
||||||
|
using Artemis.UI.Shared.Routing;
|
||||||
|
using Artemis.UI.Shared.Services;
|
||||||
|
using Artemis.WebClient.Workshop.Models;
|
||||||
|
using Artemis.WebClient.Workshop.Services;
|
||||||
|
using Avalonia.Threading;
|
||||||
|
using PropertyChanged.SourceGenerator;
|
||||||
|
using ReactiveUI;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Screens.Workshop.Plugins;
|
||||||
|
|
||||||
|
public partial class PluginManageViewModel : RoutableScreen<WorkshopDetailParameters>
|
||||||
|
{
|
||||||
|
private readonly ISettingsVmFactory _settingsVmFactory;
|
||||||
|
private readonly IRouter _router;
|
||||||
|
private readonly IWorkshopService _workshopService;
|
||||||
|
private readonly IPluginManagementService _pluginManagementService;
|
||||||
|
private readonly IWindowService _windowService;
|
||||||
|
[Notify] private PluginViewModel? _pluginViewModel;
|
||||||
|
[Notify] private ObservableCollection<PluginFeatureViewModel>? _pluginFeatures;
|
||||||
|
|
||||||
|
public PluginManageViewModel(ISettingsVmFactory settingsVmFactory, IRouter router, IWorkshopService workshopService, IPluginManagementService pluginManagementService, IWindowService windowService)
|
||||||
|
{
|
||||||
|
_settingsVmFactory = settingsVmFactory;
|
||||||
|
_router = router;
|
||||||
|
_workshopService = workshopService;
|
||||||
|
_pluginManagementService = pluginManagementService;
|
||||||
|
_windowService = windowService;
|
||||||
|
ParameterSource = ParameterSource.Route;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Close()
|
||||||
|
{
|
||||||
|
await _router.GoUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override async Task OnNavigating(WorkshopDetailParameters parameters, NavigationArguments args, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
InstalledEntry? installedEntry = _workshopService.GetInstalledEntry(parameters.EntryId);
|
||||||
|
if (installedEntry == null || !installedEntry.TryGetMetadata("PluginId", out Guid pluginId))
|
||||||
|
{
|
||||||
|
// TODO: Fix cancelling without this workaround, currently navigation is stopped but the page still opens
|
||||||
|
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||||
|
{
|
||||||
|
await _windowService.ShowConfirmContentDialog("Invalid plugin", "The plugin you're trying to manage is invalid or doesn't exist", "Go back", null);
|
||||||
|
await Close();
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Plugin? plugin = _pluginManagementService.GetAllPlugins().FirstOrDefault(p => p.Guid == pluginId);
|
||||||
|
if (plugin == null)
|
||||||
|
{
|
||||||
|
// TODO: Fix cancelling without this workaround, currently navigation is stopped but the page still opens
|
||||||
|
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||||
|
{
|
||||||
|
await _windowService.ShowConfirmContentDialog("Invalid plugin", "The plugin you're trying to manage is invalid or doesn't exist", "Go back", null);
|
||||||
|
await Close();
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PluginViewModel = _settingsVmFactory.PluginViewModel(plugin, ReactiveCommand.Create(() => { }));
|
||||||
|
PluginFeatures = new ObservableCollection<PluginFeatureViewModel>(plugin.Features.Select(f => _settingsVmFactory.PluginFeatureViewModel(f, false)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -20,4 +20,6 @@ public interface IWorkshopService
|
|||||||
void Initialize();
|
void Initialize();
|
||||||
|
|
||||||
public record WorkshopStatus(bool IsReachable, string Message);
|
public record WorkshopStatus(bool IsReachable, string Message);
|
||||||
|
|
||||||
|
event EventHandler<InstalledEntry>? OnInstalledEntrySaved;
|
||||||
}
|
}
|
||||||
@ -172,6 +172,8 @@ public class WorkshopService : IWorkshopService
|
|||||||
{
|
{
|
||||||
entry.Save();
|
entry.Save();
|
||||||
_entryRepository.Save(entry.Entity);
|
_entryRepository.Save(entry.Entity);
|
||||||
|
|
||||||
|
OnInstalledEntrySaved?.Invoke(this, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -231,4 +233,6 @@ public class WorkshopService : IWorkshopService
|
|||||||
_logger.Warning(e, "Failed to remove orphaned workshop entry at {Directory}", directory);
|
_logger.Warning(e, "Failed to remove orphaned workshop entry at {Directory}", directory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public event EventHandler<InstalledEntry>? OnInstalledEntrySaved;
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user