mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Added layout archive generation
This commit is contained in:
parent
59fe1df40f
commit
6b4e84c95a
@ -47,7 +47,6 @@
|
|||||||
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.4.0" />
|
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.4.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
<PackageReference Include="RGB.NET.Core" Version="$(RGBDotNetVersion)" />
|
<PackageReference Include="RGB.NET.Core" Version="$(RGBDotNetVersion)" />
|
||||||
<PackageReference Include="RGB.NET.Layout" Version="$(RGBDotNetVersion)" />
|
|
||||||
<PackageReference Include="RGB.NET.Presets" Version="$(RGBDotNetVersion)" />
|
<PackageReference Include="RGB.NET.Presets" Version="$(RGBDotNetVersion)" />
|
||||||
<PackageReference Include="Serilog" Version="3.0.1" />
|
<PackageReference Include="Serilog" Version="3.0.1" />
|
||||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
|
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
|
||||||
@ -65,4 +64,10 @@
|
|||||||
<None Include="Resources/intro-profile.json" CopyToOutputDirectory="PreserveNewest" />
|
<None Include="Resources/intro-profile.json" CopyToOutputDirectory="PreserveNewest" />
|
||||||
<None Include="DefaultLayouts/**" CopyToOutputDirectory="PreserveNewest" />
|
<None Include="DefaultLayouts/**" CopyToOutputDirectory="PreserveNewest" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="RGB.NET.Layout">
|
||||||
|
<HintPath>..\..\..\RGB.NET\bin\net7.0\RGB.NET.Layout.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@ -64,7 +64,12 @@ public class ArtemisLedLayout
|
|||||||
|
|
||||||
private void ApplyLogicalLayout(LayoutCustomLedDataLogicalLayout logicalLayout)
|
private void ApplyLogicalLayout(LayoutCustomLedDataLogicalLayout logicalLayout)
|
||||||
{
|
{
|
||||||
|
string? layoutDirectory = Path.GetDirectoryName(DeviceLayout.FilePath);
|
||||||
|
|
||||||
LogicalName = logicalLayout.Name;
|
LogicalName = logicalLayout.Name;
|
||||||
Image = new Uri(Path.Combine(Path.GetDirectoryName(DeviceLayout.FilePath)!, logicalLayout.Image!), UriKind.Absolute);
|
if (layoutDirectory != null && logicalLayout.Image != null)
|
||||||
|
Image = new Uri(Path.Combine(layoutDirectory, logicalLayout.Image!), UriKind.Absolute);
|
||||||
|
else
|
||||||
|
Image = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -29,4 +29,9 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj" />
|
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="RGB.NET.Layout">
|
||||||
|
<HintPath>..\..\..\RGB.NET\bin\net7.0\RGB.NET.Layout.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@ -44,7 +44,6 @@
|
|||||||
<PackageReference Include="ReactiveUI" Version="19.5.1" />
|
<PackageReference Include="ReactiveUI" Version="19.5.1" />
|
||||||
<PackageReference Include="ReactiveUI.Validation" Version="3.1.7" />
|
<PackageReference Include="ReactiveUI.Validation" Version="3.1.7" />
|
||||||
<PackageReference Include="RGB.NET.Core" Version="$(RGBDotNetVersion)" />
|
<PackageReference Include="RGB.NET.Core" Version="$(RGBDotNetVersion)" />
|
||||||
<PackageReference Include="RGB.NET.Layout" Version="$(RGBDotNetVersion)" />
|
|
||||||
<PackageReference Include="SkiaSharp" Version="$(SkiaSharpVersion)" />
|
<PackageReference Include="SkiaSharp" Version="$(SkiaSharpVersion)" />
|
||||||
<PackageReference Include="Splat.DryIoc" Version="14.7.1" />
|
<PackageReference Include="Splat.DryIoc" Version="14.7.1" />
|
||||||
<PackageReference Include="TextMateSharp.Grammars" Version="1.0.56" />
|
<PackageReference Include="TextMateSharp.Grammars" Version="1.0.56" />
|
||||||
@ -53,4 +52,10 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AvaloniaResource Include="Assets\**" />
|
<AvaloniaResource Include="Assets\**" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="RGB.NET.Layout">
|
||||||
|
<HintPath>..\..\..\RGB.NET\bin\net7.0\RGB.NET.Layout.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@ -1,9 +1,7 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reactive.Disposables;
|
using System.Reactive.Disposables;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using Artemis.UI.Shared;
|
using Artemis.UI.Shared;
|
||||||
using Artemis.UI.Shared.Services;
|
|
||||||
using Avalonia.Media.Imaging;
|
using Avalonia.Media.Imaging;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using PropertyChanged.SourceGenerator;
|
using PropertyChanged.SourceGenerator;
|
||||||
|
|||||||
@ -9,6 +9,7 @@ using Artemis.Core.Services;
|
|||||||
using Artemis.UI.Screens.Workshop.Layout.Dialogs;
|
using Artemis.UI.Screens.Workshop.Layout.Dialogs;
|
||||||
using Artemis.UI.Shared;
|
using Artemis.UI.Shared;
|
||||||
using Artemis.UI.Shared.Services;
|
using Artemis.UI.Shared.Services;
|
||||||
|
using Artemis.WebClient.Workshop.Handlers.UploadHandlers;
|
||||||
using PropertyChanged.SourceGenerator;
|
using PropertyChanged.SourceGenerator;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
using ReactiveUI.Validation.Extensions;
|
using ReactiveUI.Validation.Extensions;
|
||||||
@ -65,4 +66,14 @@ public partial class LayoutInfoViewModel : ValidatableViewModelBase
|
|||||||
if (deviceProvider != null)
|
if (deviceProvider != null)
|
||||||
DeviceProviderId = deviceProvider.Plugin.Guid;
|
DeviceProviderId = deviceProvider.Plugin.Guid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LayoutInfo ToLayoutInfo()
|
||||||
|
{
|
||||||
|
return new LayoutInfo
|
||||||
|
{
|
||||||
|
Model = Model ?? string.Empty,
|
||||||
|
Vendor = Vendor ?? string.Empty,
|
||||||
|
DeviceProviderId = DeviceProviderId
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -11,7 +11,6 @@ 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.Handlers.InstallationHandlers.Implementations;
|
|
||||||
using PropertyChanged.SourceGenerator;
|
using PropertyChanged.SourceGenerator;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
using StrawberryShake;
|
using StrawberryShake;
|
||||||
|
|||||||
@ -14,6 +14,7 @@ namespace Artemis.UI.Screens.Workshop.SubmissionWizard.Steps;
|
|||||||
|
|
||||||
public class ImagesStepViewModel : SubmissionViewModel
|
public class ImagesStepViewModel : SubmissionViewModel
|
||||||
{
|
{
|
||||||
|
private const long MAX_FILE_SIZE = 10 * 1024 * 1024; // 10 MB
|
||||||
private readonly IWindowService _windowService;
|
private readonly IWindowService _windowService;
|
||||||
private readonly SourceList<Stream> _imageStreams;
|
private readonly SourceList<Stream> _imageStreams;
|
||||||
|
|
||||||
@ -60,7 +61,14 @@ public class ImagesStepViewModel : SubmissionViewModel
|
|||||||
if (_imageStreams.Items.Any(i => i is FileStream fs && fs.Name == path))
|
if (_imageStreams.Items.Any(i => i is FileStream fs && fs.Name == path))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
FileStream stream = new(path, FileMode.Open);
|
FileStream stream = new(path, FileMode.Open, FileAccess.Read);
|
||||||
|
if (stream.Length > MAX_FILE_SIZE)
|
||||||
|
{
|
||||||
|
await _windowService.ShowConfirmContentDialog("File too big", $"File {path} exceeds maximum file size of 10 MB", "Skip file", null);
|
||||||
|
await stream.DisposeAsync();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
_imageStreams.Add(stream);
|
_imageStreams.Add(stream);
|
||||||
State.Images.Add(stream);
|
State.Images.Add(stream);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -85,39 +85,14 @@ public partial class LayoutInfoStepViewModel : SubmissionViewModel
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
layoutEntrySource.PhysicalLayout = PhysicalLayout;
|
layoutEntrySource.PhysicalLayout = PhysicalLayout;
|
||||||
// layoutEntrySource.LayoutInfo = new List<LayoutInfo>(LayoutInfo.Select(i => i.ToLayoutInfo()));
|
layoutEntrySource.LayoutInfo = new List<LayoutInfo>(LayoutInfo.Select(i => i.ToLayoutInfo()));
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(State.Name))
|
if (string.IsNullOrWhiteSpace(State.Name))
|
||||||
State.Name = layoutEntrySource.Layout.RgbLayout.Name ?? "";
|
State.Name = layoutEntrySource.Layout.RgbLayout.Name ?? "";
|
||||||
if (string.IsNullOrWhiteSpace(State.Summary))
|
if (string.IsNullOrWhiteSpace(State.Summary))
|
||||||
{
|
|
||||||
State.Summary = !string.IsNullOrWhiteSpace(layoutEntrySource.Layout.RgbLayout.Vendor)
|
State.Summary = !string.IsNullOrWhiteSpace(layoutEntrySource.Layout.RgbLayout.Vendor)
|
||||||
? $"{layoutEntrySource.Layout.RgbLayout.Vendor} {layoutEntrySource.Layout.RgbLayout.Type} device layout"
|
? $"{layoutEntrySource.Layout.RgbLayout.Vendor} {layoutEntrySource.Layout.RgbLayout.Type} device layout"
|
||||||
: $"{layoutEntrySource.Layout.RgbLayout.Type} device layout";
|
: $"{layoutEntrySource.Layout.RgbLayout.Type} device layout";
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(State.Description))
|
|
||||||
{
|
|
||||||
State.Description = $@"### Layout properties
|
|
||||||
**Name**
|
|
||||||
{layoutEntrySource.Layout.RgbLayout.Name ?? "N/A"}
|
|
||||||
**Description**
|
|
||||||
{layoutEntrySource.Layout.RgbLayout.Description ?? "N/A"}
|
|
||||||
**Author**
|
|
||||||
{layoutEntrySource.Layout.RgbLayout.Author ?? "N/A"}
|
|
||||||
**Type**
|
|
||||||
{layoutEntrySource.Layout.RgbLayout.Type}
|
|
||||||
**Vendor**
|
|
||||||
{layoutEntrySource.Layout.RgbLayout.Vendor ?? "N/A"}
|
|
||||||
**Model**
|
|
||||||
{layoutEntrySource.Layout.RgbLayout.Model ?? "N/A"}
|
|
||||||
**Shape**
|
|
||||||
{layoutEntrySource.Layout.RgbLayout.Shape}
|
|
||||||
**Width**
|
|
||||||
{layoutEntrySource.Layout.RgbLayout.Width}mm
|
|
||||||
**Height**
|
|
||||||
{layoutEntrySource.Layout.RgbLayout.Height}mm";
|
|
||||||
}
|
|
||||||
|
|
||||||
State.Categories = new List<long> {8}; // Device category, yes this could change but why would it
|
State.Categories = new List<long> {8}; // Device category, yes this could change but why would it
|
||||||
if (State.EntryId == null)
|
if (State.EntryId == null)
|
||||||
|
|||||||
@ -9,6 +9,7 @@ using System.IO;
|
|||||||
using System.Reactive.Disposables;
|
using System.Reactive.Disposables;
|
||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Artemis.UI.Exceptions;
|
||||||
using Artemis.UI.Screens.Workshop.SubmissionWizard.Models;
|
using Artemis.UI.Screens.Workshop.SubmissionWizard.Models;
|
||||||
using Artemis.UI.Shared.Extensions;
|
using Artemis.UI.Shared.Extensions;
|
||||||
using Artemis.UI.Shared.Services;
|
using Artemis.UI.Shared.Services;
|
||||||
@ -60,15 +61,23 @@ public partial class LayoutSelectionStepViewModel : SubmissionViewModel
|
|||||||
if (selected == null || selected.Length != 1)
|
if (selected == null || selected.Length != 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ArtemisLayout layout = new(selected[0], LayoutSource.User);
|
try
|
||||||
if (!layout.IsValid)
|
|
||||||
{
|
{
|
||||||
await _windowService.ShowConfirmContentDialog("Invalid layout file", "The selected file does not appear to be a valid RGB.NET layout file", cancel: null);
|
ArtemisLayout layout = new(selected[0], LayoutSource.User);
|
||||||
return;
|
if (!layout.IsValid)
|
||||||
}
|
{
|
||||||
|
await _windowService.ShowConfirmContentDialog("Failed to load layout", "The selected file does not appear to be a valid RGB.NET layout file.", "Close", null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
SelectedDevice = null;
|
SelectedDevice = null;
|
||||||
Layout = layout;
|
Layout = layout;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
await _windowService.ShowConfirmContentDialog("Failed to load layout", "The selected file does not appear to be a valid RGB.NET layout file.\r\n" + e.Message, "Close", null);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreatePreviewDevice(ArtemisLayout? layout)
|
private void CreatePreviewDevice(ArtemisLayout? layout)
|
||||||
@ -87,12 +96,55 @@ public partial class LayoutSelectionStepViewModel : SubmissionViewModel
|
|||||||
{
|
{
|
||||||
if (Layout == null)
|
if (Layout == null)
|
||||||
return;
|
return;
|
||||||
|
if (!await ValidateLayout(Layout))
|
||||||
|
return;
|
||||||
|
|
||||||
State.EntrySource = new LayoutEntrySource(Layout);
|
State.EntrySource = new LayoutEntrySource(Layout);
|
||||||
await Dispatcher.UIThread.InvokeAsync(SetDeviceImages, DispatcherPriority.Background);
|
await Dispatcher.UIThread.InvokeAsync(SetDeviceImages, DispatcherPriority.Background);
|
||||||
State.ChangeScreen<LayoutInfoStepViewModel>();
|
State.ChangeScreen<LayoutInfoStepViewModel>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<bool> ValidateLayout(ArtemisLayout? layout)
|
||||||
|
{
|
||||||
|
if (layout == null)
|
||||||
|
return true;
|
||||||
|
string? layoutPath = Path.GetDirectoryName(layout.FilePath);
|
||||||
|
if (layoutPath == null)
|
||||||
|
throw new ArtemisUIException($"Could not determine directory of {layout.FilePath}");
|
||||||
|
|
||||||
|
if (layout.LayoutCustomDeviceData.DeviceImage != null && !File.Exists(Path.Combine(layoutPath, layout.LayoutCustomDeviceData.DeviceImage)))
|
||||||
|
{
|
||||||
|
await _windowService.ShowConfirmContentDialog(
|
||||||
|
"Device image not found",
|
||||||
|
$"{Path.Combine(layoutPath, layout.LayoutCustomDeviceData.DeviceImage)} does not exist.",
|
||||||
|
"Close",
|
||||||
|
null
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (ArtemisLedLayout ledLayout in layout.Leds)
|
||||||
|
{
|
||||||
|
if (ledLayout.LayoutCustomLedData.LogicalLayouts == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
foreach (LayoutCustomLedDataLogicalLayout customData in ledLayout.LayoutCustomLedData.LogicalLayouts)
|
||||||
|
{
|
||||||
|
if (customData.Image == null || File.Exists(Path.Combine(layoutPath, customData.Image)))
|
||||||
|
continue;
|
||||||
|
await _windowService.ShowConfirmContentDialog(
|
||||||
|
$"Image not found for LED {ledLayout.RgbLayout.Id} ({customData.Name})",
|
||||||
|
$"{Path.Combine(layoutPath, customData.Image)} does not exist.",
|
||||||
|
"Close",
|
||||||
|
null
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private void SetDeviceImages()
|
private void SetDeviceImages()
|
||||||
{
|
{
|
||||||
if (Layout == null)
|
if (Layout == null)
|
||||||
@ -106,6 +158,7 @@ public partial class LayoutSelectionStepViewModel : SubmissionViewModel
|
|||||||
image.Save(deviceWithoutLeds);
|
image.Save(deviceWithoutLeds);
|
||||||
deviceWithoutLeds.Seek(0, SeekOrigin.Begin);
|
deviceWithoutLeds.Seek(0, SeekOrigin.Begin);
|
||||||
}
|
}
|
||||||
|
|
||||||
using (RenderTargetBitmap image = Layout.RenderLayout(true))
|
using (RenderTargetBitmap image = Layout.RenderLayout(true))
|
||||||
{
|
{
|
||||||
image.Save(deviceWithLeds);
|
image.Save(deviceWithLeds);
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reactive.Disposables;
|
using System.Reactive.Disposables;
|
||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
@ -132,11 +133,33 @@ public partial class UploadStepViewModel : SubmissionViewModel
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (Stream image in State.Images.ToList())
|
||||||
|
{
|
||||||
|
// Upload image
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ImageUploadResult imageUploadResult = await _workshopService.UploadEntryImage(entryId.Value, _progress, image, cancellationToken);
|
||||||
|
if (!imageUploadResult.IsSuccess)
|
||||||
|
throw new ArtemisWorkshopException(imageUploadResult.Message);
|
||||||
|
State.Images.Remove(image);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
// It's not critical if this fails
|
||||||
|
await _windowService.ShowConfirmContentDialog("Failed to upload image", "Your submission will continue, you can try upload a new image afterwards\r\n" + e.Message, "Continue", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
State.ChangeScreen<SubmitStepViewModel>();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (State.Icon == null)
|
if (State.Icon == null)
|
||||||
return entryId;
|
return entryId;
|
||||||
|
|
||||||
// Upload image
|
// Upload icon
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ImageUploadResult imageUploadResult = await _workshopService.SetEntryIcon(entryId.Value, _progress, State.Icon, cancellationToken);
|
ImageUploadResult imageUploadResult = await _workshopService.SetEntryIcon(entryId.Value, _progress, State.Icon, cancellationToken);
|
||||||
@ -146,12 +169,7 @@ public partial class UploadStepViewModel : SubmissionViewModel
|
|||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
// It's not critical if this fails
|
// It's not critical if this fails
|
||||||
await _windowService.ShowConfirmContentDialog(
|
await _windowService.ShowConfirmContentDialog("Failed to upload icon", "Your submission will continue, you can try upload a new image afterwards\r\n" + e.Message, "Continue", null);
|
||||||
"Failed to upload icon",
|
|
||||||
"Your submission will continue, you can try upload a new image afterwards\r\n" + e.Message,
|
|
||||||
"Continue",
|
|
||||||
null
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return entryId;
|
return entryId;
|
||||||
|
|||||||
@ -53,4 +53,10 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Handlers\" />
|
<Folder Include="Handlers\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="RGB.NET.Layout">
|
||||||
|
<HintPath>..\..\..\RGB.NET\bin\net7.0\RGB.NET.Layout.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@ -0,0 +1,4 @@
|
|||||||
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=handlers_005Cinstallationhandlers_005Cimplementations/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=handlers_005Cuploadhandlers_005Cimplementations/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cinterfaces/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||||
@ -1,5 +1,4 @@
|
|||||||
using Artemis.WebClient.Workshop.Handlers.InstallationHandlers.Implementations;
|
using DryIoc;
|
||||||
using DryIoc;
|
|
||||||
|
|
||||||
namespace Artemis.WebClient.Workshop.Handlers.InstallationHandlers;
|
namespace Artemis.WebClient.Workshop.Handlers.InstallationHandlers;
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ using Artemis.UI.Shared.Extensions;
|
|||||||
using Artemis.UI.Shared.Utilities;
|
using Artemis.UI.Shared.Utilities;
|
||||||
using Artemis.WebClient.Workshop.Services;
|
using Artemis.WebClient.Workshop.Services;
|
||||||
|
|
||||||
namespace Artemis.WebClient.Workshop.Handlers.InstallationHandlers.Implementations;
|
namespace Artemis.WebClient.Workshop.Handlers.InstallationHandlers;
|
||||||
|
|
||||||
public class ProfileEntryInstallationHandler : IEntryInstallationHandler
|
public class ProfileEntryInstallationHandler : IEntryInstallationHandler
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
using System.Xml.Serialization;
|
using System.IO.Compression;
|
||||||
|
using Artemis.Core;
|
||||||
using Artemis.UI.Shared.Utilities;
|
using Artemis.UI.Shared.Utilities;
|
||||||
|
using Artemis.WebClient.Workshop.Exceptions;
|
||||||
using RGB.NET.Layout;
|
using RGB.NET.Layout;
|
||||||
|
|
||||||
namespace Artemis.WebClient.Workshop.Handlers.UploadHandlers;
|
namespace Artemis.WebClient.Workshop.Handlers.UploadHandlers;
|
||||||
@ -12,14 +14,55 @@ public class LayoutEntryUploadHandler : IEntryUploadHandler
|
|||||||
if (entrySource is not LayoutEntrySource source)
|
if (entrySource is not LayoutEntrySource source)
|
||||||
throw new InvalidOperationException("Can only create releases for layouts");
|
throw new InvalidOperationException("Can only create releases for layouts");
|
||||||
|
|
||||||
// Create a copy of the layout, image paths are about to be rewritten
|
using MemoryStream archiveStream = new();
|
||||||
XmlSerializer serializer = new(typeof(DeviceLayout));
|
using MemoryStream layoutStream = new();
|
||||||
using MemoryStream ms = new();
|
|
||||||
await using StreamWriter writer = new(ms);
|
source.Layout.RgbLayout.Save(layoutStream);
|
||||||
serializer.Serialize(writer, source.Layout.RgbLayout);
|
layoutStream.Seek(0, SeekOrigin.Begin);
|
||||||
await writer.FlushAsync();
|
|
||||||
ms.Seek(0, SeekOrigin.Begin);
|
// Create an archive
|
||||||
|
string? layoutPath = Path.GetDirectoryName(source.Layout.FilePath);
|
||||||
|
if (layoutPath == null)
|
||||||
|
throw new ArtemisWorkshopException($"Could not determine directory of {source.Layout.FilePath}");
|
||||||
|
|
||||||
|
using (ZipArchive archive = new(archiveStream, ZipArchiveMode.Create, true))
|
||||||
|
{
|
||||||
|
// Add the layout to the archive
|
||||||
|
ZipArchiveEntry archiveEntry = archive.CreateEntry("layout.xml");
|
||||||
|
await using (Stream layoutArchiveStream = archiveEntry.Open())
|
||||||
|
await layoutStream.CopyToAsync(layoutArchiveStream, cancellationToken);
|
||||||
|
|
||||||
|
// Add the layout image to the archive
|
||||||
|
CopyImage(layoutPath, source.Layout.LayoutCustomDeviceData.DeviceImage, archive);
|
||||||
|
|
||||||
|
// Add the LED images to the archive
|
||||||
|
foreach (ArtemisLedLayout ledLayout in source.Layout.Leds)
|
||||||
|
{
|
||||||
|
if (ledLayout.LayoutCustomLedData.LogicalLayouts == null)
|
||||||
|
continue;
|
||||||
|
foreach (LayoutCustomLedDataLogicalLayout customData in ledLayout.LayoutCustomLedData.LogicalLayouts)
|
||||||
|
CopyImage(layoutPath, customData.Image, archive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
archiveStream.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
|
||||||
|
string filePath = Path.Combine(desktopPath, "layout-test.zip");
|
||||||
|
await using (FileStream fileStream = new(filePath, FileMode.Create, FileAccess.Write))
|
||||||
|
{
|
||||||
|
archiveStream.WriteTo(fileStream);
|
||||||
|
}
|
||||||
|
|
||||||
return new EntryUploadResult();
|
return new EntryUploadResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void CopyImage(string layoutPath, string? imagePath, ZipArchive archive)
|
||||||
|
{
|
||||||
|
if (imagePath == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
string fullPath = Path.Combine(layoutPath, imagePath);
|
||||||
|
archive.CreateEntryFromFile(fullPath, imagePath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -7,6 +7,7 @@ public interface IWorkshopService
|
|||||||
{
|
{
|
||||||
Task<Stream?> GetEntryIcon(long entryId, CancellationToken cancellationToken);
|
Task<Stream?> GetEntryIcon(long entryId, CancellationToken cancellationToken);
|
||||||
Task<ImageUploadResult> SetEntryIcon(long entryId, Progress<StreamProgress> progress, Stream icon, CancellationToken cancellationToken);
|
Task<ImageUploadResult> SetEntryIcon(long entryId, Progress<StreamProgress> progress, Stream icon, CancellationToken cancellationToken);
|
||||||
|
Task<ImageUploadResult> UploadEntryImage(long entryId, Progress<StreamProgress> progress, Stream image, CancellationToken cancellationToken);
|
||||||
Task<WorkshopStatus> GetWorkshopStatus(CancellationToken cancellationToken);
|
Task<WorkshopStatus> GetWorkshopStatus(CancellationToken cancellationToken);
|
||||||
Task<bool> ValidateWorkshopStatus(CancellationToken cancellationToken);
|
Task<bool> ValidateWorkshopStatus(CancellationToken cancellationToken);
|
||||||
Task NavigateToEntry(long entryId, EntryType entryType);
|
Task NavigateToEntry(long entryId, EntryType entryType);
|
||||||
|
|||||||
@ -56,6 +56,27 @@ public class WorkshopService : IWorkshopService
|
|||||||
return ImageUploadResult.FromSuccess();
|
return ImageUploadResult.FromSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<ImageUploadResult> UploadEntryImage(long entryId, Progress<StreamProgress> progress, Stream image, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
image.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
// Submit the archive
|
||||||
|
HttpClient client = _httpClientFactory.CreateClient(WorkshopConstants.WORKSHOP_CLIENT_NAME);
|
||||||
|
|
||||||
|
// Construct the request
|
||||||
|
MultipartFormDataContent content = new();
|
||||||
|
ProgressableStreamContent streamContent = new(image, progress);
|
||||||
|
streamContent.Headers.ContentType = new MediaTypeHeaderValue("image/png");
|
||||||
|
content.Add(streamContent, "file", "file.png");
|
||||||
|
|
||||||
|
// Submit
|
||||||
|
HttpResponseMessage response = await client.PostAsync($"entries/{entryId}/image", content, cancellationToken);
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
return ImageUploadResult.FromFailure($"{response.StatusCode} - {await response.Content.ReadAsStringAsync(cancellationToken)}");
|
||||||
|
return ImageUploadResult.FromSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task<IWorkshopService.WorkshopStatus> GetWorkshopStatus(CancellationToken cancellationToken)
|
public async Task<IWorkshopService.WorkshopStatus> GetWorkshopStatus(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user