mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-11 19:36:28 +00:00
Workshop - Implemented workshop installation, layout selection and removal
This commit is contained in:
1 parent
dad6a56238
commit
28edabae89
23 files changed
+226
-168
No files matched your search
+1
-1
@@ -5,6 +5,6 @@ namespace Artemis.WebClient.Workshop.Handlers.InstallationHandlers;
|
||||
|
||||
public interface IEntryInstallationHandler
|
||||
{
|
||||
Task<EntryInstallResult> InstallAsync(IGetEntryById_Entry entry, long releaseId, Progress<StreamProgress> progress, CancellationToken cancellationToken);
|
||||
Task<EntryInstallResult> InstallAsync(IEntryDetails entry, IRelease release, Progress<StreamProgress> progress, CancellationToken cancellationToken);
|
||||
Task<EntryUninstallResult> UninstallAsync(InstalledEntry installedEntry, CancellationToken cancellationToken);
|
||||
}
|
||||
+24
-13
@@ -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<EntryInstallResult> InstallAsync(IGetEntryById_Entry entry, long releaseId, Progress<StreamProgress> progress, CancellationToken cancellationToken)
|
||||
public async Task<EntryInstallResult> InstallAsync(IEntryDetails entry, IRelease release, Progress<StreamProgress> 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<EntryUninstallResult> UninstallAsync(InstalledEntry installedEntry, CancellationToken cancellationToken)
|
||||
public Task<EntryUninstallResult> 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());
|
||||
}
|
||||
}
|
||||
+7
-9
@@ -19,7 +19,7 @@ public class ProfileEntryInstallationHandler : IEntryInstallationHandler
|
||||
_workshopService = workshopService;
|
||||
}
|
||||
|
||||
public async Task<EntryInstallResult> InstallAsync(IGetEntryById_Entry entry, long releaseId, Progress<StreamProgress> progress, CancellationToken cancellationToken)
|
||||
public async Task<EntryInstallResult> InstallAsync(IEntryDetails entry, IRelease release, Progress<StreamProgress> 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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ApplyLayout(ArtemisDevice device, ArtemisLayout layout)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
device.ApplyLayout(layout, device.DeviceProvider.CreateMissingLedsSupported, device.DeviceProvider.RemoveExcessiveLedsSupported);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -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<string>? deviceIds) && deviceIds.Contains(device.Identifier);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures the provided device to use this layout provider.
|
||||
/// </summary>
|
||||
/// <param name="device">The device to apply the provider to.</param>
|
||||
public void ConfigureDevice(ArtemisDevice device)
|
||||
/// <param name="entry">The workshop entry to use as a layout.</param>
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -41,13 +41,17 @@ fragment entryDetails on Entry {
|
||||
...category
|
||||
}
|
||||
latestRelease {
|
||||
id
|
||||
version
|
||||
downloadSize
|
||||
md5Hash
|
||||
createdAt
|
||||
...release
|
||||
}
|
||||
images {
|
||||
...image
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragment release on Release {
|
||||
id
|
||||
version
|
||||
downloadSize
|
||||
md5Hash
|
||||
createdAt
|
||||
}
|
||||
@@ -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
|
||||
/// <summary>
|
||||
/// Returns the directory info of the entry, where any files would be stored if applicable.
|
||||
/// </summary>
|
||||
/// <param name="rootDirectory">A value indicating whether or not to return the root directory of the entry, and not the version.</param>
|
||||
/// <returns>The directory info of the entry, where any files would be stored if applicable.</returns>
|
||||
public DirectoryInfo GetDirectory(bool rootDirectory = false)
|
||||
/// <returns>The directory info of the directory.</returns>
|
||||
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)}"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the directory info of a release of this entry, where any files would be stored if applicable.
|
||||
/// </summary>
|
||||
/// <param name="release">The release to use, if none provided the current release is used.</param>
|
||||
/// <returns>The directory info of the directory.</returns>
|
||||
public DirectoryInfo GetReleaseDirectory(IRelease? release = null)
|
||||
{
|
||||
return new DirectoryInfo(Path.Combine(GetDirectory().FullName, StringUtilities.UrlFriendly(release?.Version ?? ReleaseVersion)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the provided release to the installed entry.
|
||||
/// </summary>
|
||||
/// <param name="release">The release to apply.</param>
|
||||
public void ApplyRelease(IRelease release)
|
||||
{
|
||||
ReleaseId = release.Id;
|
||||
ReleaseVersion = release.Version;
|
||||
InstalledAt = DateTimeOffset.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -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<InstalledEntry> 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);
|
||||
}
|
||||
@@ -133,7 +133,7 @@ public class WorkshopService : IWorkshopService
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public InstalledEntry CreateInstalledEntry(IGetEntryById_Entry entry)
|
||||
public void AddOrUpdateInstalledEntry(InstalledEntry entry, IRelease release)
|
||||
{
|
||||
return new InstalledEntry(entry);
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
Reference in new issue
Block a user