diff --git a/src/Artemis.Core/Extensions/DirectoryInfoExtensions.cs b/src/Artemis.Core/Extensions/DirectoryInfoExtensions.cs
deleted file mode 100644
index 5a6864273..000000000
--- a/src/Artemis.Core/Extensions/DirectoryInfoExtensions.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using System.IO;
-
-namespace Artemis.Core;
-
-internal static class DirectoryInfoExtensions
-{
- public static void CopyFilesRecursively(this DirectoryInfo source, DirectoryInfo target)
- {
- foreach (DirectoryInfo dir in source.GetDirectories())
- CopyFilesRecursively(dir, target.CreateSubdirectory(dir.Name));
- foreach (FileInfo file in source.GetFiles())
- file.CopyTo(Path.Combine(target.FullName, file.Name));
- }
-
- public static void DeleteRecursively(this DirectoryInfo baseDir)
- {
- if (!baseDir.Exists)
- return;
-
- foreach (DirectoryInfo dir in baseDir.EnumerateDirectories())
- DeleteRecursively(dir);
- FileInfo[] files = baseDir.GetFiles();
- foreach (FileInfo file in files)
- {
- file.IsReadOnly = false;
- file.Delete();
- }
-
- baseDir.Delete();
- }
-}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Surface/ArtemisDevice.cs b/src/Artemis.Core/Models/Surface/ArtemisDevice.cs
index 34a10d0fc..6a42aacc7 100644
--- a/src/Artemis.Core/Models/Surface/ArtemisDevice.cs
+++ b/src/Artemis.Core/Models/Surface/ArtemisDevice.cs
@@ -398,14 +398,6 @@ public class ArtemisDevice : CorePropertyChanged
}
}
- ///
- /// Invokes the event
- ///
- protected virtual void OnDeviceUpdated()
- {
- DeviceUpdated?.Invoke(this, EventArgs.Empty);
- }
-
///
/// Applies the provided layout to the device
///
@@ -418,7 +410,7 @@ public class ArtemisDevice : CorePropertyChanged
/// A boolean indicating whether to remove excess LEDs present in the device but missing
/// in the layout
///
- internal void ApplyLayout(ArtemisLayout? layout, bool createMissingLeds, bool removeExcessiveLeds)
+ public void ApplyLayout(ArtemisLayout? layout, bool createMissingLeds, bool removeExcessiveLeds)
{
if (layout != null && layout.IsValid && createMissingLeds && !DeviceProvider.CreateMissingLedsSupported)
throw new ArtemisCoreException($"Cannot apply layout with {nameof(createMissingLeds)} set to true because the device provider does not support it");
@@ -445,6 +437,14 @@ public class ArtemisDevice : CorePropertyChanged
CalculateRenderProperties();
}
+ ///
+ /// Invokes the event
+ ///
+ protected virtual void OnDeviceUpdated()
+ {
+ DeviceUpdated?.Invoke(this, EventArgs.Empty);
+ }
+
private void ClearLayout()
{
if (Layout == null)
diff --git a/src/Artemis.Core/Providers/CustomPathLayoutProvider.cs b/src/Artemis.Core/Providers/CustomPathLayoutProvider.cs
index 61f47119a..bd2ba228b 100644
--- a/src/Artemis.Core/Providers/CustomPathLayoutProvider.cs
+++ b/src/Artemis.Core/Providers/CustomPathLayoutProvider.cs
@@ -1,17 +1,8 @@
-using Artemis.Core.Services;
-using RGB.NET.Layout;
-
-namespace Artemis.Core.Providers;
+namespace Artemis.Core.Providers;
public class CustomPathLayoutProvider : ILayoutProvider
{
public static string LayoutType = "CustomPath";
- private readonly IDeviceService _deviceService;
-
- public CustomPathLayoutProvider(IDeviceService deviceService)
- {
- _deviceService = deviceService;
- }
///
public ArtemisLayout? GetDeviceLayout(ArtemisDevice device)
@@ -34,7 +25,7 @@ public class CustomPathLayoutProvider : ILayoutProvider
}
///
- /// Configures the provided device to use this layout provider.
+ /// Configures the provided device to use this layout provider.
///
/// The device to apply the provider to.
/// The path to the custom layout.
@@ -42,7 +33,5 @@ public class CustomPathLayoutProvider : ILayoutProvider
{
device.LayoutSelection.Type = LayoutType;
device.LayoutSelection.Parameter = path;
- _deviceService.SaveDevice(device);
- _deviceService.LoadDeviceLayout(device);
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Providers/DefaultLayoutProvider.cs b/src/Artemis.Core/Providers/DefaultLayoutProvider.cs
index 3ab899bed..8c709ab27 100644
--- a/src/Artemis.Core/Providers/DefaultLayoutProvider.cs
+++ b/src/Artemis.Core/Providers/DefaultLayoutProvider.cs
@@ -1,17 +1,9 @@
-using Artemis.Core.Services;
-
-namespace Artemis.Core.Providers;
+namespace Artemis.Core.Providers;
public class DefaultLayoutProvider : ILayoutProvider
{
public static string LayoutType = "Default";
- private readonly IDeviceService _deviceService;
- public DefaultLayoutProvider(IDeviceService deviceService)
- {
- _deviceService = deviceService;
- }
-
///
public ArtemisLayout? GetDeviceLayout(ArtemisDevice device)
{
@@ -30,22 +22,20 @@ public class DefaultLayoutProvider : ILayoutProvider
else
device.ApplyLayout(layout, device.DeviceProvider.CreateMissingLedsSupported, device.DeviceProvider.RemoveExcessiveLedsSupported);
}
-
+
///
public bool IsMatch(ArtemisDevice device)
{
return device.LayoutSelection.Type == LayoutType;
}
-
+
///
- /// Configures the provided device to use this layout provider.
+ /// Configures the provided device to use this layout provider.
///
/// The device to apply the provider to.
public void ConfigureDevice(ArtemisDevice device)
{
device.LayoutSelection.Type = LayoutType;
device.LayoutSelection.Parameter = null;
- _deviceService.SaveDevice(device);
- _deviceService.LoadDeviceLayout(device);
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Providers/Interfaces/ILayoutProvider.cs b/src/Artemis.Core/Providers/Interfaces/ILayoutProvider.cs
index fe8d421c2..3cccfe39d 100644
--- a/src/Artemis.Core/Providers/Interfaces/ILayoutProvider.cs
+++ b/src/Artemis.Core/Providers/Interfaces/ILayoutProvider.cs
@@ -1,15 +1,15 @@
namespace Artemis.Core.Providers;
///
-/// Represents a class that can provide Artemis layouts for devices.
+/// Represents a class that can provide Artemis layouts for devices.
///
public interface ILayoutProvider
{
///
- /// If available, loads an Artemis layout for the provided device.
+ /// If available, loads an Artemis layout for the provided device.
///
/// The device to load the layout for.
- /// The resulting layout if one was available; otherwise .
+ /// The resulting layout if one was available; otherwise .
ArtemisLayout? GetDeviceLayout(ArtemisDevice device);
void ApplyLayout(ArtemisDevice device, ArtemisLayout layout);
diff --git a/src/Artemis.Core/Providers/NoneLayoutProvider.cs b/src/Artemis.Core/Providers/NoneLayoutProvider.cs
index 35de69ee6..59c13391b 100644
--- a/src/Artemis.Core/Providers/NoneLayoutProvider.cs
+++ b/src/Artemis.Core/Providers/NoneLayoutProvider.cs
@@ -1,17 +1,9 @@
-using Artemis.Core.Services;
-
-namespace Artemis.Core.Providers;
+namespace Artemis.Core.Providers;
public class NoneLayoutProvider : ILayoutProvider
{
- private readonly IDeviceService _deviceService;
public static string LayoutType = "None";
- public NoneLayoutProvider(IDeviceService deviceService)
- {
- _deviceService = deviceService;
- }
-
///
public ArtemisLayout? GetDeviceLayout(ArtemisDevice device)
{
@@ -23,22 +15,20 @@ public class NoneLayoutProvider : ILayoutProvider
{
device.ApplyLayout(null, false, false);
}
-
+
///
public bool IsMatch(ArtemisDevice device)
{
return device.LayoutSelection.Type == LayoutType;
}
-
+
///
- /// Configures the provided device to use this layout provider.
+ /// Configures the provided device to use this layout provider.
///
/// The device to apply the provider to.
public void ConfigureDevice(ArtemisDevice device)
{
device.LayoutSelection.Type = LayoutType;
device.LayoutSelection.Parameter = null;
- _deviceService.SaveDevice(device);
- _deviceService.LoadDeviceLayout(device);
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Services/DeviceService.cs b/src/Artemis.Core/Services/DeviceService.cs
index 3a17d0bdc..afc949bba 100644
--- a/src/Artemis.Core/Services/DeviceService.cs
+++ b/src/Artemis.Core/Services/DeviceService.cs
@@ -20,7 +20,7 @@ internal class DeviceService : IDeviceService
private readonly IPluginManagementService _pluginManagementService;
private readonly IDeviceRepository _deviceRepository;
private readonly Lazy _renderService;
- private readonly LazyEnumerable _layoutProviders;
+ private readonly Func> _getLayoutProviders;
private readonly List _enabledDevices = new();
private readonly List _devices = new();
@@ -28,13 +28,13 @@ internal class DeviceService : IDeviceService
IPluginManagementService pluginManagementService,
IDeviceRepository deviceRepository,
Lazy renderService,
- LazyEnumerable layoutProviders)
+ Func> getLayoutProviders)
{
_logger = logger;
_pluginManagementService = pluginManagementService;
_deviceRepository = deviceRepository;
_renderService = renderService;
- _layoutProviders = layoutProviders;
+ _getLayoutProviders = getLayoutProviders;
EnabledDevices = new ReadOnlyCollection(_enabledDevices);
Devices = new ReadOnlyCollection(_devices);
@@ -167,7 +167,7 @@ internal class DeviceService : IDeviceService
///
public void LoadDeviceLayout(ArtemisDevice device)
{
- ILayoutProvider? provider = _layoutProviders.FirstOrDefault(p => p.IsMatch(device));
+ ILayoutProvider? provider = _getLayoutProviders().FirstOrDefault(p => p.IsMatch(device));
if (provider == null)
_logger.Warning("Could not find a layout provider for type {LayoutType} of device {Device}", device.LayoutSelection.Type, device);
diff --git a/src/Artemis.Core/Services/PluginManagementService.cs b/src/Artemis.Core/Services/PluginManagementService.cs
index 639aa014f..8194cef44 100644
--- a/src/Artemis.Core/Services/PluginManagementService.cs
+++ b/src/Artemis.Core/Services/PluginManagementService.cs
@@ -57,7 +57,7 @@ internal class PluginManagementService : IPluginManagementService
// Remove the old directory if it exists
if (Directory.Exists(pluginDirectory.FullName))
- pluginDirectory.DeleteRecursively();
+ pluginDirectory.Delete(true);
// Extract everything in the same archive directory to the unique plugin directory
Utilities.CreateAccessibleDirectory(pluginDirectory.FullName);
diff --git a/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/CustomLayoutViewModel.cs b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/CustomLayoutViewModel.cs
index 624fa376e..86b73c8a8 100644
--- a/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/CustomLayoutViewModel.cs
+++ b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/CustomLayoutViewModel.cs
@@ -1,6 +1,7 @@
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.Core.Providers;
+using Artemis.Core.Services;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.Builders;
@@ -11,11 +12,12 @@ public class CustomLayoutViewModel : ViewModelBase, ILayoutProviderViewModel
{
private readonly CustomPathLayoutProvider _layoutProvider;
- public CustomLayoutViewModel(IWindowService windowService, INotificationService notificationService, CustomPathLayoutProvider layoutProvider)
+ public CustomLayoutViewModel(IWindowService windowService, INotificationService notificationService, IDeviceService deviceService, CustomPathLayoutProvider layoutProvider)
{
_layoutProvider = layoutProvider;
_windowService = windowService;
_notificationService = notificationService;
+ _deviceService = deviceService;
}
///
@@ -32,11 +34,13 @@ public class CustomLayoutViewModel : ViewModelBase, ILayoutProviderViewModel
private readonly IWindowService _windowService;
private readonly INotificationService _notificationService;
+ private readonly IDeviceService _deviceService;
public void ClearCustomLayout()
{
_layoutProvider.ConfigureDevice(Device, null);
-
+ Save();
+
_notificationService.CreateNotification()
.WithMessage("Cleared imported layout.")
.WithSeverity(NotificationSeverity.Informational);
@@ -52,7 +56,8 @@ public class CustomLayoutViewModel : ViewModelBase, ILayoutProviderViewModel
if (files?.Length > 0)
{
_layoutProvider.ConfigureDevice(Device, files[0]);
-
+ Save();
+
_notificationService.CreateNotification()
.WithTitle("Imported layout")
.WithMessage($"File loaded from {files[0]}")
@@ -64,5 +69,12 @@ public class CustomLayoutViewModel : ViewModelBase, ILayoutProviderViewModel
public void Apply()
{
_layoutProvider.ConfigureDevice(Device, null);
+ Save();
+ }
+
+ private void Save()
+ {
+ _deviceService.SaveDevice(Device);
+ _deviceService.LoadDeviceLayout(Device);
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/DefaultLayoutViewModel.cs b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/DefaultLayoutViewModel.cs
index 78d4b9b0f..b4c2e5547 100644
--- a/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/DefaultLayoutViewModel.cs
+++ b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/DefaultLayoutViewModel.cs
@@ -1,5 +1,6 @@
using Artemis.Core;
using Artemis.Core.Providers;
+using Artemis.Core.Services;
using Artemis.UI.Shared;
namespace Artemis.UI.Screens.Device.Layout.LayoutProviders;
@@ -7,10 +8,12 @@ namespace Artemis.UI.Screens.Device.Layout.LayoutProviders;
public class DefaultLayoutViewModel : ViewModelBase, ILayoutProviderViewModel
{
private readonly DefaultLayoutProvider _layoutProvider;
+ private readonly IDeviceService _deviceService;
- public DefaultLayoutViewModel(DefaultLayoutProvider layoutProvider)
+ public DefaultLayoutViewModel(DefaultLayoutProvider layoutProvider, IDeviceService deviceService)
{
_layoutProvider = layoutProvider;
+ _deviceService = deviceService;
}
///
@@ -28,5 +31,12 @@ public class DefaultLayoutViewModel : ViewModelBase, ILayoutProviderViewModel
public void Apply()
{
_layoutProvider.ConfigureDevice(Device);
+ Save();
+ }
+
+ private void Save()
+ {
+ _deviceService.SaveDevice(Device);
+ _deviceService.LoadDeviceLayout(Device);
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/NoneLayoutViewModel.cs b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/NoneLayoutViewModel.cs
index 466c13f31..ab3ae1214 100644
--- a/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/NoneLayoutViewModel.cs
+++ b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/NoneLayoutViewModel.cs
@@ -1,5 +1,6 @@
using Artemis.Core;
using Artemis.Core.Providers;
+using Artemis.Core.Services;
using Artemis.UI.Shared;
namespace Artemis.UI.Screens.Device.Layout.LayoutProviders;
@@ -7,10 +8,12 @@ namespace Artemis.UI.Screens.Device.Layout.LayoutProviders;
public class NoneLayoutViewModel : ViewModelBase, ILayoutProviderViewModel
{
private readonly NoneLayoutProvider _layoutProvider;
+ private readonly IDeviceService _deviceService;
- public NoneLayoutViewModel(NoneLayoutProvider layoutProvider)
+ public NoneLayoutViewModel(NoneLayoutProvider layoutProvider, IDeviceService deviceService)
{
_layoutProvider = layoutProvider;
+ _deviceService = deviceService;
}
///
@@ -28,5 +31,12 @@ public class NoneLayoutViewModel : ViewModelBase, ILayoutProviderViewModel
public void Apply()
{
_layoutProvider.ConfigureDevice(Device);
+ Save();
+ }
+
+ private void Save()
+ {
+ _deviceService.SaveDevice(Device);
+ _deviceService.LoadDeviceLayout(Device);
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/WorkshopLayoutView.axaml b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/WorkshopLayoutView.axaml
index 50de1a677..75bbd544d 100644
--- a/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/WorkshopLayoutView.axaml
+++ b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/WorkshopLayoutView.axaml
@@ -2,13 +2,45 @@
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:services="clr-namespace:Artemis.WebClient.Workshop.Services;assembly=Artemis.WebClient.Workshop"
+ xmlns:layoutProviders="clr-namespace:Artemis.UI.Screens.Device.Layout.LayoutProviders"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
- x:Class="Artemis.UI.Screens.Device.Layout.LayoutProviders.WorkshopLayoutView">
+ x:Class="Artemis.UI.Screens.Device.Layout.LayoutProviders.WorkshopLayoutView"
+ x:DataType="layoutProviders:WorkshopLayoutViewModel">
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/WorkshopLayoutView.axaml.cs b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/WorkshopLayoutView.axaml.cs
index fc27fb9aa..f0f9766c1 100644
--- a/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/WorkshopLayoutView.axaml.cs
+++ b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/WorkshopLayoutView.axaml.cs
@@ -1,8 +1,9 @@
using Avalonia.Controls;
+using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.Device.Layout.LayoutProviders;
-public partial class WorkshopLayoutView : UserControl
+public partial class WorkshopLayoutView : ReactiveUserControl
{
public WorkshopLayoutView()
{
diff --git a/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/WorkshopLayoutViewModel.cs b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/WorkshopLayoutViewModel.cs
index 948f8fe29..e3c3f9f05 100644
--- a/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/WorkshopLayoutViewModel.cs
+++ b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/WorkshopLayoutViewModel.cs
@@ -1,17 +1,33 @@
-using Artemis.Core;
+using System.Collections.ObjectModel;
+using System;
+using System.Linq;
+using System.Reactive.Disposables;
+using Artemis.Core;
using Artemis.Core.Providers;
+using Artemis.Core.Services;
using Artemis.UI.Shared;
+using Artemis.WebClient.Workshop;
using Artemis.WebClient.Workshop.Providers;
+using Artemis.WebClient.Workshop.Services;
+using PropertyChanged.SourceGenerator;
+using ReactiveUI;
namespace Artemis.UI.Screens.Device.Layout.LayoutProviders;
-public class WorkshopLayoutViewModel : ViewModelBase, ILayoutProviderViewModel
+public partial class WorkshopLayoutViewModel : ActivatableViewModelBase, ILayoutProviderViewModel
{
+ [Notify] private InstalledEntry? _selectedEntry;
private readonly WorkshopLayoutProvider _layoutProvider;
+ private readonly IDeviceService _deviceService;
- public WorkshopLayoutViewModel(WorkshopLayoutProvider layoutProvider)
+ public WorkshopLayoutViewModel(WorkshopLayoutProvider layoutProvider, IWorkshopService workshopService, IDeviceService deviceService)
{
_layoutProvider = layoutProvider;
+ _deviceService = deviceService;
+ Entries = new ObservableCollection(workshopService.GetInstalledEntries().Where(e => e.EntryType == EntryType.Layout));
+
+ this.WhenAnyValue(vm => vm.SelectedEntry).Subscribe(ApplyEntry);
+ this.WhenActivated((CompositeDisposable _) => SelectedEntry = Entries.FirstOrDefault(e => e.EntryId.ToString() == Device.LayoutSelection.Parameter));
}
///
@@ -19,6 +35,8 @@ public class WorkshopLayoutViewModel : ViewModelBase, ILayoutProviderViewModel
public ArtemisDevice Device { get; set; } = null!;
+ public ObservableCollection Entries { get; }
+
///
public string Name => "Workshop";
@@ -28,6 +46,21 @@ public class WorkshopLayoutViewModel : ViewModelBase, ILayoutProviderViewModel
///
public void Apply()
{
- _layoutProvider.ConfigureDevice(Device);
+ _layoutProvider.ConfigureDevice(Device, null);
+ Save();
+ }
+
+ private void ApplyEntry(InstalledEntry? entry)
+ {
+ if (entry == null || Device.LayoutSelection.Parameter == entry.EntryId.ToString())
+ return;
+ _layoutProvider.ConfigureDevice(Device, entry);
+ Save();
+ }
+
+ private void Save()
+ {
+ _deviceService.SaveDevice(Device);
+ _deviceService.LoadDeviceLayout(Device);
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryReleasesViewModel.cs b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryReleasesViewModel.cs
index 11608288a..02cbef240 100644
--- a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryReleasesViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryReleasesViewModel.cs
@@ -45,7 +45,7 @@ public class EntryReleasesViewModel : ViewModelBase
return;
IEntryInstallationHandler installationHandler = _factory.CreateHandler(Entry.EntryType);
- EntryInstallResult result = await installationHandler.InstallAsync(Entry, Entry.LatestRelease.Id, new Progress(), cancellationToken);
+ EntryInstallResult result = await installationHandler.InstallAsync(Entry, Entry.LatestRelease, new Progress(), cancellationToken);
if (result.IsSuccess)
_notificationService.CreateNotification().WithTitle($"{Entry.EntryType.Humanize(LetterCasing.Sentence)} installed").WithSeverity(NotificationSeverity.Success).Show();
else
diff --git a/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/IEntryInstallationHandler.cs b/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/IEntryInstallationHandler.cs
index d8e7ba951..0e4e2d4f4 100644
--- a/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/IEntryInstallationHandler.cs
+++ b/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/IEntryInstallationHandler.cs
@@ -5,6 +5,6 @@ namespace Artemis.WebClient.Workshop.Handlers.InstallationHandlers;
public interface IEntryInstallationHandler
{
- Task InstallAsync(IGetEntryById_Entry entry, long releaseId, Progress progress, CancellationToken cancellationToken);
+ Task InstallAsync(IEntryDetails entry, IRelease release, Progress progress, CancellationToken cancellationToken);
Task UninstallAsync(InstalledEntry installedEntry, CancellationToken cancellationToken);
}
\ No newline at end of file
diff --git a/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/LayoutEntryInstallationHandler.cs b/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/LayoutEntryInstallationHandler.cs
index 13a2e2318..a0adb4d0c 100644
--- a/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/LayoutEntryInstallationHandler.cs
+++ b/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/LayoutEntryInstallationHandler.cs
@@ -1,10 +1,10 @@
-using System.Data;
-using System.IO.Compression;
+using System.IO.Compression;
using Artemis.Core;
+using Artemis.Core.Providers;
using Artemis.Core.Services;
using Artemis.UI.Shared.Extensions;
-using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Utilities;
+using Artemis.WebClient.Workshop.Providers;
using Artemis.WebClient.Workshop.Services;
namespace Artemis.WebClient.Workshop.Handlers.InstallationHandlers;
@@ -14,15 +14,17 @@ public class LayoutEntryInstallationHandler : IEntryInstallationHandler
private readonly IHttpClientFactory _httpClientFactory;
private readonly IWorkshopService _workshopService;
private readonly IDeviceService _deviceService;
+ private readonly DefaultLayoutProvider _defaultLayoutProvider;
- public LayoutEntryInstallationHandler(IHttpClientFactory httpClientFactory, IWorkshopService workshopService, IDeviceService deviceService)
+ public LayoutEntryInstallationHandler(IHttpClientFactory httpClientFactory, IWorkshopService workshopService, IDeviceService deviceService, DefaultLayoutProvider defaultLayoutProvider)
{
_httpClientFactory = httpClientFactory;
_workshopService = workshopService;
_deviceService = deviceService;
+ _defaultLayoutProvider = defaultLayoutProvider;
}
- public async Task InstallAsync(IGetEntryById_Entry entry, long releaseId, Progress progress, CancellationToken cancellationToken)
+ public async Task InstallAsync(IEntryDetails entry, IRelease release, Progress progress, CancellationToken cancellationToken)
{
using MemoryStream stream = new();
@@ -30,7 +32,7 @@ public class LayoutEntryInstallationHandler : IEntryInstallationHandler
try
{
HttpClient client = _httpClientFactory.CreateClient(WorkshopConstants.WORKSHOP_CLIENT_NAME);
- await client.DownloadDataAsync($"releases/download/{releaseId}", stream, progress, cancellationToken);
+ await client.DownloadDataAsync($"releases/download/{release.Id}", stream, progress, cancellationToken);
}
catch (Exception e)
{
@@ -38,8 +40,8 @@ public class LayoutEntryInstallationHandler : IEntryInstallationHandler
}
// Ensure there is an installed entry
- InstalledEntry installedEntry = _workshopService.GetInstalledEntry(entry) ?? _workshopService.CreateInstalledEntry(entry);
- DirectoryInfo entryDirectory = installedEntry.GetDirectory();
+ InstalledEntry installedEntry = _workshopService.GetInstalledEntry(entry) ?? new InstalledEntry(entry, release);
+ DirectoryInfo entryDirectory = installedEntry.GetReleaseDirectory(release);
// If the folder already exists, remove it so that if the layout now contains less files, old things dont stick around
if (entryDirectory.Exists)
@@ -53,7 +55,11 @@ public class LayoutEntryInstallationHandler : IEntryInstallationHandler
ArtemisLayout layout = new(Path.Combine(entryDirectory.FullName, "layout.xml"));
if (layout.IsValid)
+ {
+ installedEntry.ApplyRelease(release);
+ _workshopService.SaveInstalledEntry(installedEntry);
return EntryInstallResult.FromSuccess(layout);
+ }
// If the layout ended up being invalid yoink it out again, shoooo
entryDirectory.Delete(true);
@@ -61,21 +67,26 @@ public class LayoutEntryInstallationHandler : IEntryInstallationHandler
return EntryInstallResult.FromFailure("Layout failed to load because it is invalid");
}
- public async Task UninstallAsync(InstalledEntry installedEntry, CancellationToken cancellationToken)
+ public Task UninstallAsync(InstalledEntry installedEntry, CancellationToken cancellationToken)
{
// Remove the layout from any devices currently using it
foreach (ArtemisDevice device in _deviceService.Devices)
{
+ if (device.LayoutSelection.Type == WorkshopLayoutProvider.LayoutType && device.LayoutSelection.Parameter == installedEntry.EntryId.ToString())
+ {
+ _defaultLayoutProvider.ConfigureDevice(device);
+ _deviceService.SaveDevice(device);
+ _deviceService.LoadDeviceLayout(device);
+ }
}
// Remove from filesystem
- DirectoryInfo directory = installedEntry.GetDirectory(true);
+ DirectoryInfo directory = installedEntry.GetDirectory();
if (directory.Exists)
- directory.Delete();
+ directory.Delete(true);
// Remove entry
_workshopService.RemoveInstalledEntry(installedEntry);
-
- return EntryUninstallResult.FromSuccess();
+ return Task.FromResult(EntryUninstallResult.FromSuccess());
}
}
\ No newline at end of file
diff --git a/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/ProfileEntryInstallationHandler.cs b/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/ProfileEntryInstallationHandler.cs
index b322e40cd..c44956700 100644
--- a/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/ProfileEntryInstallationHandler.cs
+++ b/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/ProfileEntryInstallationHandler.cs
@@ -19,7 +19,7 @@ public class ProfileEntryInstallationHandler : IEntryInstallationHandler
_workshopService = workshopService;
}
- public async Task InstallAsync(IGetEntryById_Entry entry, long releaseId, Progress progress, CancellationToken cancellationToken)
+ public async Task InstallAsync(IEntryDetails entry, IRelease release, Progress progress, CancellationToken cancellationToken)
{
using MemoryStream stream = new();
@@ -27,7 +27,7 @@ public class ProfileEntryInstallationHandler : IEntryInstallationHandler
try
{
HttpClient client = _httpClientFactory.CreateClient(WorkshopConstants.WORKSHOP_CLIENT_NAME);
- await client.DownloadDataAsync($"releases/download/{releaseId}", stream, progress, cancellationToken);
+ await client.DownloadDataAsync($"releases/download/{release}", stream, progress, cancellationToken);
}
catch (Exception e)
{
@@ -45,13 +45,13 @@ public class ProfileEntryInstallationHandler : IEntryInstallationHandler
installedEntry.SetMetadata("ProfileId", overwritten.ProfileId);
// Update the release and return the profile configuration
- UpdateRelease(releaseId, installedEntry);
+ UpdateRelease(installedEntry, release);
return EntryInstallResult.FromSuccess(overwritten);
}
}
// Ensure there is an installed entry
- installedEntry ??= _workshopService.CreateInstalledEntry(entry);
+ installedEntry ??= new InstalledEntry(entry, release);
// Add the profile as a fresh import
ProfileCategory category = _profileService.ProfileCategories.FirstOrDefault(c => c.Name == "Workshop") ?? _profileService.CreateProfileCategory("Workshop", true);
@@ -59,7 +59,7 @@ public class ProfileEntryInstallationHandler : IEntryInstallationHandler
installedEntry.SetMetadata("ProfileId", imported.ProfileId);
// Update the release and return the profile configuration
- UpdateRelease(releaseId, installedEntry);
+ UpdateRelease(installedEntry, release);
return EntryInstallResult.FromSuccess(imported);
}
@@ -89,11 +89,9 @@ public class ProfileEntryInstallationHandler : IEntryInstallationHandler
}, cancellationToken);
}
- private void UpdateRelease(long releaseId, InstalledEntry installedEntry)
+ private void UpdateRelease(InstalledEntry installedEntry, IRelease release)
{
- installedEntry.ReleaseId = releaseId;
- installedEntry.ReleaseVersion = "TODO";
- installedEntry.InstalledAt = DateTimeOffset.UtcNow;
+ installedEntry.ApplyRelease(release);
_workshopService.SaveInstalledEntry(installedEntry);
}
}
\ No newline at end of file
diff --git a/src/Artemis.WebClient.Workshop/Providers/WorkshopLayoutProvider.cs b/src/Artemis.WebClient.Workshop/Providers/WorkshopLayoutProvider.cs
index 6544ff437..4d028f4a1 100644
--- a/src/Artemis.WebClient.Workshop/Providers/WorkshopLayoutProvider.cs
+++ b/src/Artemis.WebClient.Workshop/Providers/WorkshopLayoutProvider.cs
@@ -1,6 +1,5 @@
using Artemis.Core;
using Artemis.Core.Providers;
-using Artemis.Core.Services;
using Artemis.WebClient.Workshop.Services;
namespace Artemis.WebClient.Workshop.Providers;
@@ -8,34 +7,31 @@ namespace Artemis.WebClient.Workshop.Providers;
public class WorkshopLayoutProvider : ILayoutProvider
{
public static string LayoutType = "Workshop";
-
- private readonly IDeviceService _deviceService;
private readonly IWorkshopService _workshopService;
- public WorkshopLayoutProvider(IDeviceService deviceService, IWorkshopService workshopService)
+ public WorkshopLayoutProvider(IWorkshopService workshopService)
{
- _deviceService = deviceService;
_workshopService = workshopService;
}
///
public ArtemisLayout? GetDeviceLayout(ArtemisDevice device)
{
- InstalledEntry? layoutEntry = _workshopService.GetInstalledEntries().FirstOrDefault(e => e.EntryType == EntryType.Layout && MatchesDevice(e, device));
+ InstalledEntry? layoutEntry = _workshopService.GetInstalledEntries().FirstOrDefault(e => e.EntryId.ToString() == device.LayoutSelection.Parameter);
if (layoutEntry == null)
return null;
- string layoutPath = Path.Combine(Constants.WorkshopFolder, layoutEntry.EntryId.ToString(), "layout.xml");
+ string layoutPath = Path.Combine(layoutEntry.GetReleaseDirectory().FullName, "layout.xml");
if (!File.Exists(layoutPath))
return null;
-
+
return new ArtemisLayout(layoutPath);
}
///
public void ApplyLayout(ArtemisDevice device, ArtemisLayout layout)
{
- throw new NotImplementedException();
+ device.ApplyLayout(layout, device.DeviceProvider.CreateMissingLedsSupported, device.DeviceProvider.RemoveExcessiveLedsSupported);
}
///
@@ -44,20 +40,17 @@ public class WorkshopLayoutProvider : ILayoutProvider
return device.LayoutSelection.Type == LayoutType;
}
- private bool MatchesDevice(InstalledEntry entry, ArtemisDevice device)
- {
- return entry.TryGetMetadata("DeviceId", out HashSet? deviceIds) && deviceIds.Contains(device.Identifier);
- }
-
///
/// Configures the provided device to use this layout provider.
///
/// The device to apply the provider to.
- public void ConfigureDevice(ArtemisDevice device)
+ /// The workshop entry to use as a layout.
+ public void ConfigureDevice(ArtemisDevice device, InstalledEntry? entry)
{
+ if (entry != null && entry.EntryType != EntryType.Layout)
+ throw new InvalidOperationException($"Cannot use a workshop entry of type {entry.EntryType} as a layout");
+
device.LayoutSelection.Type = LayoutType;
- device.LayoutSelection.Parameter = null;
- _deviceService.SaveDevice(device);
- _deviceService.LoadDeviceLayout(device);
+ device.LayoutSelection.Parameter = entry?.EntryId.ToString();
}
}
\ No newline at end of file
diff --git a/src/Artemis.WebClient.Workshop/Queries/Fragments.graphql b/src/Artemis.WebClient.Workshop/Queries/Fragments.graphql
index 9dc7949ea..a66c3a660 100644
--- a/src/Artemis.WebClient.Workshop/Queries/Fragments.graphql
+++ b/src/Artemis.WebClient.Workshop/Queries/Fragments.graphql
@@ -41,13 +41,17 @@ fragment entryDetails on Entry {
...category
}
latestRelease {
- id
- version
- downloadSize
- md5Hash
- createdAt
+ ...release
}
images {
...image
}
-}
\ No newline at end of file
+}
+
+fragment release on Release {
+ id
+ version
+ downloadSize
+ md5Hash
+ createdAt
+}
\ No newline at end of file
diff --git a/src/Artemis.WebClient.Workshop/Services/InstalledEntry.cs b/src/Artemis.WebClient.Workshop/Services/InstalledEntry.cs
index 1ac3069e2..6373a8d2a 100644
--- a/src/Artemis.WebClient.Workshop/Services/InstalledEntry.cs
+++ b/src/Artemis.WebClient.Workshop/Services/InstalledEntry.cs
@@ -14,7 +14,7 @@ public class InstalledEntry
Load();
}
- public InstalledEntry(IGetEntryById_Entry entry)
+ public InstalledEntry(IEntryDetails entry, IRelease release)
{
Entity = new EntryEntity();
@@ -23,6 +23,9 @@ public class InstalledEntry
Author = entry.Author;
Name = entry.Name;
+ InstalledAt = DateTimeOffset.Now;
+ ReleaseId = release.Id;
+ ReleaseVersion = release.Version;
}
public long EntryId { get; set; }
@@ -110,14 +113,30 @@ public class InstalledEntry
///
/// Returns the directory info of the entry, where any files would be stored if applicable.
///
- /// A value indicating whether or not to return the root directory of the entry, and not the version.
- /// The directory info of the entry, where any files would be stored if applicable.
- public DirectoryInfo GetDirectory(bool rootDirectory = false)
+ /// The directory info of the directory.
+ public DirectoryInfo GetDirectory()
{
- if (rootDirectory)
- return new DirectoryInfo(Path.Combine(Constants.WorkshopFolder, EntryId.ToString()));
-
- string safeVersion = Path.GetInvalidFileNameChars().Aggregate(ReleaseVersion, (current, c) => current.Replace(c, '-'));
- return new DirectoryInfo(Path.Combine(Constants.WorkshopFolder, EntryId.ToString(), safeVersion));
+ return new DirectoryInfo(Path.Combine(Constants.WorkshopFolder, $"{EntryId}-{StringUtilities.UrlFriendly(Name)}"));
+ }
+
+ ///
+ /// Returns the directory info of a release of this entry, where any files would be stored if applicable.
+ ///
+ /// The release to use, if none provided the current release is used.
+ /// The directory info of the directory.
+ public DirectoryInfo GetReleaseDirectory(IRelease? release = null)
+ {
+ return new DirectoryInfo(Path.Combine(GetDirectory().FullName, StringUtilities.UrlFriendly(release?.Version ?? ReleaseVersion)));
+ }
+
+ ///
+ /// Applies the provided release to the installed entry.
+ ///
+ /// The release to apply.
+ public void ApplyRelease(IRelease release)
+ {
+ ReleaseId = release.Id;
+ ReleaseVersion = release.Version;
+ InstalledAt = DateTimeOffset.UtcNow;
}
}
\ 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 f1041f2d0..3b36c6b2d 100644
--- a/src/Artemis.WebClient.Workshop/Services/Interfaces/IWorkshopService.cs
+++ b/src/Artemis.WebClient.Workshop/Services/Interfaces/IWorkshopService.cs
@@ -1,4 +1,3 @@
-using Artemis.UI.Shared.Utilities;
using Artemis.WebClient.Workshop.Handlers.UploadHandlers;
namespace Artemis.WebClient.Workshop.Services;
@@ -13,11 +12,9 @@ public interface IWorkshopService
Task NavigateToEntry(long entryId, EntryType entryType);
List GetInstalledEntries();
- InstalledEntry? GetInstalledEntry(IGetEntryById_Entry entry);
- InstalledEntry CreateInstalledEntry(IGetEntryById_Entry entry);
+ InstalledEntry? GetInstalledEntry(IEntryDetails entry);
void RemoveInstalledEntry(InstalledEntry installedEntry);
void SaveInstalledEntry(InstalledEntry entry);
-
public record WorkshopStatus(bool IsReachable, string Message);
}
\ 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 3257c5c0e..920d29f8e 100644
--- a/src/Artemis.WebClient.Workshop/Services/WorkshopService.cs
+++ b/src/Artemis.WebClient.Workshop/Services/WorkshopService.cs
@@ -133,7 +133,7 @@ public class WorkshopService : IWorkshopService
}
///
- public InstalledEntry? GetInstalledEntry(IGetEntryById_Entry entry)
+ public InstalledEntry? GetInstalledEntry(IEntryDetails entry)
{
EntryEntity? entity = _entryRepository.GetByEntryId(entry.Id);
if (entity == null)
@@ -143,9 +143,9 @@ public class WorkshopService : IWorkshopService
}
///
- public InstalledEntry CreateInstalledEntry(IGetEntryById_Entry entry)
+ public void AddOrUpdateInstalledEntry(InstalledEntry entry, IRelease release)
{
- return new InstalledEntry(entry);
+ throw new NotImplementedException();
}
///