diff --git a/src/Artemis.UI.Shared/Routing/Routable/RoutableScreenOfTParam.cs b/src/Artemis.UI.Shared/Routing/Routable/RoutableScreenOfTParam.cs
index bbe71fe2a..52e8a5e48 100644
--- a/src/Artemis.UI.Shared/Routing/Routable/RoutableScreenOfTParam.cs
+++ b/src/Artemis.UI.Shared/Routing/Routable/RoutableScreenOfTParam.cs
@@ -13,6 +13,11 @@ namespace Artemis.UI.Shared.Routing;
/// The type of parameters the screen expects. It must have a parameterless constructor.
public abstract class RoutableScreen : RoutableScreen, IRoutableScreen where TParam : new()
{
+ ///
+ /// Gets or sets the parameter source of the screen.
+ ///
+ protected ParameterSource ParameterSource { get; set; } = ParameterSource.Segment;
+
///
/// Called while navigating to this screen.
///
@@ -26,15 +31,16 @@ public abstract class RoutableScreen : RoutableScreen, IRoutableScreen w
{
return Task.CompletedTask;
}
-
+
async Task IRoutableScreen.InternalOnNavigating(NavigationArguments args, CancellationToken cancellationToken)
{
Func activator = GetParameterActivator();
- if (args.SegmentParameters.Length != _parameterPropertyCount)
- throw new ArtemisRoutingException($"Did not retrieve the required amount of parameters, expects {_parameterPropertyCount}, got {args.SegmentParameters.Length}.");
+ object[] routeParameters = ParameterSource == ParameterSource.Segment ? args.SegmentParameters : args.RouteParameters;
+ 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(parameters, args, cancellationToken);
}
@@ -97,4 +103,20 @@ public abstract class RoutableScreen : RoutableScreen, IRoutableScreen w
}
#endregion
+}
+
+///
+/// Enum representing the source of parameters in the RoutableScreen class.
+///
+public enum ParameterSource
+{
+ ///
+ /// Represents the source where parameters are obtained from the segment of the route.
+ ///
+ Segment,
+
+ ///
+ /// Represents the source where parameters are obtained from the entire route.
+ ///
+ Route
}
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/Routing/Router/Router.cs b/src/Artemis.UI.Shared/Routing/Router/Router.cs
index 3f1263a29..269d51fce 100644
--- a/src/Artemis.UI.Shared/Routing/Router/Router.cs
+++ b/src/Artemis.UI.Shared/Routing/Router/Router.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Reactive.Subjects;
using System.Threading.Tasks;
using Artemis.Core;
@@ -72,7 +73,13 @@ internal class Router : CorePropertyChanged, IRouter, IDisposable
///
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();
// 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);
}
+
+
+ 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 remainingCurrentPathParts = pathParts.Take(pathParts.Length - upCount);
+ IEnumerable remainingNavigatePathParts = navigateParts.Skip(upCount);
+
+ return string.Join("/", remainingCurrentPathParts.Concat(remainingNavigatePathParts));
+ }
private void MainWindowServiceOnMainWindowOpened(object? sender, EventArgs e)
{
diff --git a/src/Artemis.UI/Artemis.UI.csproj b/src/Artemis.UI/Artemis.UI.csproj
index c52465960..1714435f1 100644
--- a/src/Artemis.UI/Artemis.UI.csproj
+++ b/src/Artemis.UI/Artemis.UI.csproj
@@ -65,5 +65,6 @@
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Routing/Routes.cs b/src/Artemis.UI/Routing/Routes.cs
index e64c17868..cac359e3d 100644
--- a/src/Artemis.UI/Routing/Routes.cs
+++ b/src/Artemis.UI/Routing/Routes.cs
@@ -30,6 +30,7 @@ namespace Artemis.UI.Routing
new RouteRegistration("entries", [
new RouteRegistration("plugins", [
new RouteRegistration("details/{entryId:long}", [
+ new RouteRegistration("manage"),
new RouteRegistration("releases/{releaseId:long}")
])
]),
@@ -40,6 +41,7 @@ namespace Artemis.UI.Routing
]),
new RouteRegistration("layouts", [
new RouteRegistration("details/{entryId:long}", [
+ new RouteRegistration("manage"),
new RouteRegistration("releases/{releaseId:long}")
])
])
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryInfoView.axaml b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryInfoView.axaml
index 2d0ecbffa..52f1b8dd3 100644
--- a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryInfoView.axaml
+++ b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryInfoView.axaml
@@ -32,7 +32,6 @@
-
Updated
+
+
+ Manage installation
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryInfoView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryInfoView.axaml.cs
index 5c6ec0a0c..2d9a96dc7 100644
--- a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryInfoView.axaml.cs
+++ b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryInfoView.axaml.cs
@@ -1,10 +1,11 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
+using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.Workshop.Entries.Details;
-public partial class EntryInfoView : UserControl
+public partial class EntryInfoView : ReactiveUserControl
{
public EntryInfoView()
{
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryInfoViewModel.cs b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryInfoViewModel.cs
index cd37606ea..8fc0f1b33 100644
--- a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryInfoViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryInfoViewModel.cs
@@ -1,29 +1,54 @@
using System;
using System.Linq;
+using System.Reactive.Disposables;
+using System.Reactive.Linq;
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.UI.Shared;
+using Artemis.UI.Shared.Routing;
using Artemis.UI.Shared.Services;
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;
-public class EntryInfoViewModel : ViewModelBase
+public partial class EntryInfoViewModel : ActivatableViewModelBase
{
+ private readonly IRouter _router;
private readonly INotificationService _notificationService;
- public IEntryDetails Entry { get; }
- public DateTimeOffset? UpdatedAt { get; }
+ [Notify] private bool _canBeManaged;
- public EntryInfoViewModel(IEntryDetails entry, INotificationService notificationService)
+ public EntryInfoViewModel(IEntryDetails entry, IRouter router, INotificationService notificationService, IWorkshopService workshopService)
{
+ _router = router;
_notificationService = notificationService;
Entry = entry;
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(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()
{
await Shared.UI.Clipboard.SetTextAsync($"{WorkshopConstants.WORKSHOP_URL}/entries/{Entry.Id}/{StringUtilities.UrlFriendly(Entry.Name)}");
_notificationService.CreateNotification().WithTitle("Copied share link to clipboard.").Show();
}
+
+ public async Task GoToManage()
+ {
+ await _router.Navigate("/manage");
+ }
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListItemView.axaml b/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListItemView.axaml
index 91932033a..712d7fc68 100644
--- a/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListItemView.axaml
+++ b/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListItemView.axaml
@@ -79,11 +79,11 @@
-
+
installed
-
+
update available
diff --git a/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseItemView.axaml b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseItemView.axaml
new file mode 100644
index 000000000..1a328d64a
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseItemView.axaml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Created
+
+
+
+
+
+
diff --git a/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseItemView.axaml.cs b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseItemView.axaml.cs
new file mode 100644
index 000000000..ffc325a79
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseItemView.axaml.cs
@@ -0,0 +1,11 @@
+using Avalonia.ReactiveUI;
+
+namespace Artemis.UI.Screens.Workshop.EntryReleases;
+
+public partial class EntryReleaseItemView : ReactiveUserControl
+{
+ public EntryReleaseItemView()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseItemViewModel.cs b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseItemViewModel.cs
new file mode 100644
index 000000000..bafa65507
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseItemViewModel.cs
@@ -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(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;
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseView.axaml b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseView.axaml
index 6bdededd8..3185f1a05 100644
--- a/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseView.axaml
+++ b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseView.axaml
@@ -46,7 +46,7 @@
Release info
-
+
-
- Install
-
-
+
+
+ Install
+
+
+ Re-install
+
+
+
diff --git a/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseViewModel.cs b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseViewModel.cs
index a44cc6e8d..32b1de42a 100644
--- a/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseViewModel.cs
@@ -8,6 +8,7 @@ using Artemis.UI.Shared.Services.Builders;
using Artemis.UI.Shared.Utilities;
using Artemis.WebClient.Workshop;
using Artemis.WebClient.Workshop.Handlers.InstallationHandlers;
+using Artemis.WebClient.Workshop.Services;
using PropertyChanged.SourceGenerator;
using StrawberryShake;
@@ -19,21 +20,25 @@ public partial class EntryReleaseViewModel : RoutableScreen _progress = new();
[Notify] private IGetReleaseById_Release? _release;
[Notify] private float _installProgress;
[Notify] private bool _installationInProgress;
+ [Notify] private bool _isCurrentVersion;
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;
_router = router;
_notificationService = notificationService;
_windowService = windowService;
+ _workshopService = workshopService;
_factory = factory;
_progress.ProgressChanged += (_, f) => InstallProgress = f.ProgressPercentage;
}
@@ -56,18 +61,32 @@ public partial class EntryReleaseViewModel : RoutableScreen result = await _client.GetReleaseById.ExecuteAsync(parameters.ReleaseId, cancellationToken);
Release = result.Data?.Release;
+ IsCurrentVersion = Release != null && _workshopService.GetInstalledEntry(Release.Entry.Id)?.ReleaseId == Release.Id;
}
#region Overrides of RoutableScreen
diff --git a/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleasesView.axaml b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleasesView.axaml
index cf62f0e5b..0e0346d83 100644
--- a/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleasesView.axaml
+++ b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleasesView.axaml
@@ -2,38 +2,12 @@
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: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"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.Workshop.EntryReleases.EntryReleasesView"
- x:DataType="entryReleases:EntryReleasesViewModel">
-
-
-
-
-
-
+ x:DataType="entryReleases:EntryReleasesViewModel">
Releases
-
-
-
-
-
-
-
-
-
- Created
-
-
-
-
-
-
-
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleasesViewModel.cs b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleasesViewModel.cs
index c21b841dc..6b2b344f8 100644
--- a/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleasesViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleasesViewModel.cs
@@ -16,38 +16,34 @@ namespace Artemis.UI.Screens.Workshop.EntryReleases;
public partial class EntryReleasesViewModel : ActivatableViewModelBase
{
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 getEntryReleaseItemViewModel)
{
_router = router;
Entry = entry;
- Releases = Entry.Releases.OrderByDescending(r => r.CreatedAt).Take(5).Cast().ToList();
+ Releases = Entry.Releases.OrderByDescending(r => r.CreatedAt).Take(5).Select(r => getEntryReleaseItemViewModel(r)).ToList();
NavigateToRelease = ReactiveCommand.CreateFromTask(ExecuteNavigateToRelease);
this.WhenActivated(d =>
{
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)
.DisposeWith(d);
this.WhenAnyValue(vm => vm.SelectedRelease)
.WhereNotNull()
- .Subscribe(s => ExecuteNavigateToRelease(s))
+ .Subscribe(s => ExecuteNavigateToRelease(s.Release))
.DisposeWith(d);
});
}
public IEntryDetails Entry { get; }
- public List Releases { get; }
-
+ public List Releases { get; }
public ReactiveCommand NavigateToRelease { get; }
- public Func> OnInstallationStarted { get; set; }
- public Func? OnInstallationFinished { get; set; }
-
private async Task ExecuteNavigateToRelease(IRelease release)
{
switch (Entry.EntryType)
diff --git a/src/Artemis.UI/Screens/Workshop/Layout/LayoutDetailsViewModel.cs b/src/Artemis.UI/Screens/Workshop/Layout/LayoutDetailsViewModel.cs
index c03896bf8..f4d797310 100644
--- a/src/Artemis.UI/Screens/Workshop/Layout/LayoutDetailsViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/Layout/LayoutDetailsViewModel.cs
@@ -1,20 +1,11 @@
using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using Artemis.Core;
-using Artemis.Core.Services;
using Artemis.UI.Screens.Workshop.Entries.Details;
using Artemis.UI.Screens.Workshop.EntryReleases;
-using Artemis.UI.Screens.Workshop.Layout.Dialogs;
using Artemis.UI.Screens.Workshop.Parameters;
using Artemis.UI.Shared.Routing;
-using Artemis.UI.Shared.Services;
using Artemis.WebClient.Workshop;
-using Artemis.WebClient.Workshop.Models;
-using Artemis.WebClient.Workshop.Services;
using PropertyChanged.SourceGenerator;
using StrawberryShake;
@@ -23,8 +14,6 @@ namespace Artemis.UI.Screens.Workshop.Layout;
public partial class LayoutDetailsViewModel : RoutableHostScreen
{
private readonly IWorkshopClient _client;
- private readonly IDeviceService _deviceService;
- private readonly IWindowService _windowService;
private readonly Func _getEntryInfoViewModel;
private readonly Func _getEntryReleasesViewModel;
private readonly Func _getEntryImagesViewModel;
@@ -34,16 +23,12 @@ public partial class LayoutDetailsViewModel : RoutableHostScreen getEntryInfoViewModel,
Func getEntryReleasesViewModel,
Func getEntryImagesViewModel)
{
_client = client;
- _deviceService = deviceService;
- _windowService = windowService;
_getEntryInfoViewModel = getEntryInfoViewModel;
_getEntryReleasesViewModel = getEntryReleasesViewModel;
_getEntryImagesViewModel = getEntryImagesViewModel;
@@ -70,28 +55,6 @@ public partial class LayoutDetailsViewModel : RoutableHostScreen 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();
- }
- }
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Layout/LayoutManageView.axaml b/src/Artemis.UI/Screens/Workshop/Layout/LayoutManageView.axaml
new file mode 100644
index 000000000..af6b25fc2
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/Layout/LayoutManageView.axaml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+ Manage layout
+
+
+
+
+
+ This layout is made for devices of type
+ .
+ Unfortunately, none were detected.
+
+
+
+ Select the devices on which you would like to apply the downloaded layout.
+
+
+
+
+
+
+
+
+
+
+
+ Apply
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Layout/LayoutManageView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Layout/LayoutManageView.axaml.cs
new file mode 100644
index 000000000..bc26260c9
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/Layout/LayoutManageView.axaml.cs
@@ -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
+{
+ public LayoutManageView()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Layout/LayoutManageViewModel.cs b/src/Artemis.UI/Screens/Workshop/Layout/LayoutManageViewModel.cs
new file mode 100644
index 000000000..9ce907d57
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/Layout/LayoutManageViewModel.cs
@@ -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
+{
+ 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? _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 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(_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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Plugins/Dialogs/PluginDialogView.axaml b/src/Artemis.UI/Screens/Workshop/Plugins/Dialogs/PluginDialogView.axaml
deleted file mode 100644
index 3c0336a37..000000000
--- a/src/Artemis.UI/Screens/Workshop/Plugins/Dialogs/PluginDialogView.axaml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
- Plugin features
-
-
-
-
-
diff --git a/src/Artemis.UI/Screens/Workshop/Plugins/Dialogs/PluginDialogView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Plugins/Dialogs/PluginDialogView.axaml.cs
deleted file mode 100644
index 19329553e..000000000
--- a/src/Artemis.UI/Screens/Workshop/Plugins/Dialogs/PluginDialogView.axaml.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using Avalonia.ReactiveUI;
-
-namespace Artemis.UI.Screens.Workshop.Plugins.Dialogs;
-
-public partial class PluginDialogView : ReactiveUserControl
-{
- public PluginDialogView()
- {
- InitializeComponent();
- }
-}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Plugins/Dialogs/PluginDialogViewModel.cs b/src/Artemis.UI/Screens/Workshop/Plugins/Dialogs/PluginDialogViewModel.cs
deleted file mode 100644
index 863417326..000000000
--- a/src/Artemis.UI/Screens/Workshop/Plugins/Dialogs/PluginDialogViewModel.cs
+++ /dev/null
@@ -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()));
- PluginFeatures = new ObservableCollection(plugin.Features.Select(f => settingsVmFactory.PluginFeatureViewModel(f, false)));
- }
-
- public PluginViewModel PluginViewModel { get; }
- public ObservableCollection PluginFeatures { get; }
-}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Plugins/PluginDetailsViewModel.cs b/src/Artemis.UI/Screens/Workshop/Plugins/PluginDetailsViewModel.cs
index a1b6e0939..cfab9166c 100644
--- a/src/Artemis.UI/Screens/Workshop/Plugins/PluginDetailsViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/Plugins/PluginDetailsViewModel.cs
@@ -1,20 +1,13 @@
using System;
-using System.Collections.Generic;
using System.Collections.ObjectModel;
-using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using Artemis.Core;
-using Artemis.Core.Services;
using Artemis.UI.Screens.Workshop.Entries.Details;
using Artemis.UI.Screens.Workshop.Entries.List;
using Artemis.UI.Screens.Workshop.EntryReleases;
using Artemis.UI.Screens.Workshop.Parameters;
-using Artemis.UI.Screens.Workshop.Plugins.Dialogs;
using Artemis.UI.Shared.Routing;
-using Artemis.UI.Shared.Services;
using Artemis.WebClient.Workshop;
-using Artemis.WebClient.Workshop.Models;
using PropertyChanged.SourceGenerator;
using StrawberryShake;
@@ -23,8 +16,6 @@ namespace Artemis.UI.Screens.Workshop.Plugins;
public partial class PluginDetailsViewModel : RoutableHostScreen
{
private readonly IWorkshopClient _client;
- private readonly IWindowService _windowService;
- private readonly IPluginManagementService _pluginManagementService;
private readonly Func _getEntryInfoViewModel;
private readonly Func _getEntryReleasesViewModel;
private readonly Func _getEntryImagesViewModel;
@@ -35,16 +26,12 @@ public partial class PluginDetailsViewModel : RoutableHostScreen? _dependants;
public PluginDetailsViewModel(IWorkshopClient client,
- IWindowService windowService,
- IPluginManagementService pluginManagementService,
PluginDescriptionViewModel pluginDescriptionViewModel,
Func getEntryInfoViewModel,
Func getEntryReleasesViewModel,
Func getEntryImagesViewModel)
{
_client = client;
- _windowService = windowService;
- _pluginManagementService = pluginManagementService;
_getEntryInfoViewModel = getEntryInfoViewModel;
_getEntryReleasesViewModel = getEntryReleasesViewModel;
_getEntryImagesViewModel = getEntryImagesViewModel;
@@ -72,35 +59,6 @@ public partial class PluginDetailsViewModel : RoutableHostScreen 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();
- }
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Plugins/PluginManageView.axaml b/src/Artemis.UI/Screens/Workshop/Plugins/PluginManageView.axaml
new file mode 100644
index 000000000..c6fe8c059
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/Plugins/PluginManageView.axaml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+ Manage plugin
+
+
+
+
+
+
+
+
+
+ Plugin features
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Plugins/PluginManageView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Plugins/PluginManageView.axaml.cs
new file mode 100644
index 000000000..d5136c54e
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/Plugins/PluginManageView.axaml.cs
@@ -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
+{
+ public PluginManageView()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Plugins/PluginManageViewModel.cs b/src/Artemis.UI/Screens/Workshop/Plugins/PluginManageViewModel.cs
new file mode 100644
index 000000000..9bd558217
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/Plugins/PluginManageViewModel.cs
@@ -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
+{
+ 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? _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();
+ }
+
+ ///
+ 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(plugin.Features.Select(f => _settingsVmFactory.PluginFeatureViewModel(f, false)));
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.WebClient.Workshop/Services/Interfaces/IWorkshopService.cs b/src/Artemis.WebClient.Workshop/Services/Interfaces/IWorkshopService.cs
index ea61b3b6d..304f4ef05 100644
--- a/src/Artemis.WebClient.Workshop/Services/Interfaces/IWorkshopService.cs
+++ b/src/Artemis.WebClient.Workshop/Services/Interfaces/IWorkshopService.cs
@@ -20,4 +20,6 @@ public interface IWorkshopService
void Initialize();
public record WorkshopStatus(bool IsReachable, string Message);
+
+ event EventHandler? OnInstalledEntrySaved;
}
\ No newline at end of file
diff --git a/src/Artemis.WebClient.Workshop/Services/WorkshopService.cs b/src/Artemis.WebClient.Workshop/Services/WorkshopService.cs
index 95b90ccc5..715a5a1bb 100644
--- a/src/Artemis.WebClient.Workshop/Services/WorkshopService.cs
+++ b/src/Artemis.WebClient.Workshop/Services/WorkshopService.cs
@@ -172,6 +172,8 @@ public class WorkshopService : IWorkshopService
{
entry.Save();
_entryRepository.Save(entry.Entity);
+
+ OnInstalledEntrySaved?.Invoke(this, entry);
}
///
@@ -231,4 +233,6 @@ public class WorkshopService : IWorkshopService
_logger.Warning(e, "Failed to remove orphaned workshop entry at {Directory}", directory);
}
}
+
+ public event EventHandler? OnInstalledEntrySaved;
}
\ No newline at end of file