diff --git a/src/Artemis.Core/Artemis.Core.csproj b/src/Artemis.Core/Artemis.Core.csproj
index dafe7ff82..68c2ce582 100644
--- a/src/Artemis.Core/Artemis.Core.csproj
+++ b/src/Artemis.Core/Artemis.Core.csproj
@@ -47,7 +47,6 @@
-
@@ -65,4 +64,10 @@
+
+
+
+ ..\..\..\RGB.NET\bin\net7.0\RGB.NET.Layout.dll
+
+
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Surface/Layout/ArtemisLedLayout.cs b/src/Artemis.Core/Models/Surface/Layout/ArtemisLedLayout.cs
index 18fc2490f..55e237b5d 100644
--- a/src/Artemis.Core/Models/Surface/Layout/ArtemisLedLayout.cs
+++ b/src/Artemis.Core/Models/Surface/Layout/ArtemisLedLayout.cs
@@ -64,7 +64,12 @@ public class ArtemisLedLayout
private void ApplyLogicalLayout(LayoutCustomLedDataLogicalLayout logicalLayout)
{
+ string? layoutDirectory = Path.GetDirectoryName(DeviceLayout.FilePath);
+
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;
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/Artemis.UI.Shared.csproj b/src/Artemis.UI.Shared/Artemis.UI.Shared.csproj
index bc8e31ad8..e37523b1d 100644
--- a/src/Artemis.UI.Shared/Artemis.UI.Shared.csproj
+++ b/src/Artemis.UI.Shared/Artemis.UI.Shared.csproj
@@ -29,4 +29,9 @@
+
+
+ ..\..\..\RGB.NET\bin\net7.0\RGB.NET.Layout.dll
+
+
diff --git a/src/Artemis.UI/Artemis.UI.csproj b/src/Artemis.UI/Artemis.UI.csproj
index 0c9471912..e4a63012a 100644
--- a/src/Artemis.UI/Artemis.UI.csproj
+++ b/src/Artemis.UI/Artemis.UI.csproj
@@ -44,7 +44,6 @@
-
@@ -53,4 +52,10 @@
+
+
+
+ ..\..\..\RGB.NET\bin\net7.0\RGB.NET.Layout.dll
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Image/ImageSubmissionViewModel.cs b/src/Artemis.UI/Screens/Workshop/Image/ImageSubmissionViewModel.cs
index 5b0c03cbe..c431bc2cf 100644
--- a/src/Artemis.UI/Screens/Workshop/Image/ImageSubmissionViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/Image/ImageSubmissionViewModel.cs
@@ -1,9 +1,7 @@
using System.IO;
using System.Reactive.Disposables;
-using System.Threading.Tasks;
using System.Windows.Input;
using Artemis.UI.Shared;
-using Artemis.UI.Shared.Services;
using Avalonia.Media.Imaging;
using Avalonia.Threading;
using PropertyChanged.SourceGenerator;
diff --git a/src/Artemis.UI/Screens/Workshop/Layout/LayoutInfoViewModel.cs b/src/Artemis.UI/Screens/Workshop/Layout/LayoutInfoViewModel.cs
index 22ee6a750..b34228082 100644
--- a/src/Artemis.UI/Screens/Workshop/Layout/LayoutInfoViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/Layout/LayoutInfoViewModel.cs
@@ -9,6 +9,7 @@ using Artemis.Core.Services;
using Artemis.UI.Screens.Workshop.Layout.Dialogs;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
+using Artemis.WebClient.Workshop.Handlers.UploadHandlers;
using PropertyChanged.SourceGenerator;
using ReactiveUI;
using ReactiveUI.Validation.Extensions;
@@ -24,7 +25,7 @@ public partial class LayoutInfoViewModel : ValidatableViewModelBase
[Notify] private Guid _deviceProviderId;
[Notify] private string? _deviceProviderIdInput;
[Notify] private ICommand? _remove;
-
+
public LayoutInfoViewModel(ArtemisLayout layout,
IDeviceService deviceService,
IWindowService windowService,
@@ -65,4 +66,14 @@ public partial class LayoutInfoViewModel : ValidatableViewModelBase
if (deviceProvider != null)
DeviceProviderId = deviceProvider.Plugin.Guid;
}
+
+ public LayoutInfo ToLayoutInfo()
+ {
+ return new LayoutInfo
+ {
+ Model = Model ?? string.Empty,
+ Vendor = Vendor ?? string.Empty,
+ DeviceProviderId = DeviceProviderId
+ };
+ }
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsViewModel.cs b/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsViewModel.cs
index 4dd85cd7f..c413c9eee 100644
--- a/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsViewModel.cs
@@ -11,7 +11,6 @@ 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.Handlers.InstallationHandlers.Implementations;
using PropertyChanged.SourceGenerator;
using ReactiveUI;
using StrawberryShake;
diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/ImagesStepViewModel.cs b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/ImagesStepViewModel.cs
index 4e0d1e7c5..411bf7fb3 100644
--- a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/ImagesStepViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/ImagesStepViewModel.cs
@@ -14,6 +14,7 @@ namespace Artemis.UI.Screens.Workshop.SubmissionWizard.Steps;
public class ImagesStepViewModel : SubmissionViewModel
{
+ private const long MAX_FILE_SIZE = 10 * 1024 * 1024; // 10 MB
private readonly IWindowService _windowService;
private readonly SourceList _imageStreams;
@@ -60,7 +61,14 @@ public class ImagesStepViewModel : SubmissionViewModel
if (_imageStreams.Items.Any(i => i is FileStream fs && fs.Name == path))
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);
State.Images.Add(stream);
}
diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Layout/LayoutInfoStepViewModel.cs b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Layout/LayoutInfoStepViewModel.cs
index 4b99fb802..23c3b293e 100644
--- a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Layout/LayoutInfoStepViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Layout/LayoutInfoStepViewModel.cs
@@ -85,40 +85,15 @@ public partial class LayoutInfoStepViewModel : SubmissionViewModel
return;
layoutEntrySource.PhysicalLayout = PhysicalLayout;
- // layoutEntrySource.LayoutInfo = new List(LayoutInfo.Select(i => i.ToLayoutInfo()));
+ layoutEntrySource.LayoutInfo = new List(LayoutInfo.Select(i => i.ToLayoutInfo()));
if (string.IsNullOrWhiteSpace(State.Name))
State.Name = layoutEntrySource.Layout.RgbLayout.Name ?? "";
if (string.IsNullOrWhiteSpace(State.Summary))
- {
State.Summary = !string.IsNullOrWhiteSpace(layoutEntrySource.Layout.RgbLayout.Vendor)
? $"{layoutEntrySource.Layout.RgbLayout.Vendor} {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 {8}; // Device category, yes this could change but why would it
if (State.EntryId == null)
State.ChangeScreen();
diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Layout/LayoutSelectionStepViewModel.cs b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Layout/LayoutSelectionStepViewModel.cs
index 9dc0899a1..69e4709c1 100644
--- a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Layout/LayoutSelectionStepViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Layout/LayoutSelectionStepViewModel.cs
@@ -9,6 +9,7 @@ using System.IO;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Threading.Tasks;
+using Artemis.UI.Exceptions;
using Artemis.UI.Screens.Workshop.SubmissionWizard.Models;
using Artemis.UI.Shared.Extensions;
using Artemis.UI.Shared.Services;
@@ -60,15 +61,23 @@ public partial class LayoutSelectionStepViewModel : SubmissionViewModel
if (selected == null || selected.Length != 1)
return;
- ArtemisLayout layout = new(selected[0], LayoutSource.User);
- if (!layout.IsValid)
+ try
{
- await _windowService.ShowConfirmContentDialog("Invalid layout file", "The selected file does not appear to be a valid RGB.NET layout file", cancel: null);
- return;
- }
+ ArtemisLayout layout = new(selected[0], LayoutSource.User);
+ 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;
- Layout = layout;
+ SelectedDevice = null;
+ 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)
@@ -87,25 +96,69 @@ public partial class LayoutSelectionStepViewModel : SubmissionViewModel
{
if (Layout == null)
return;
+ if (!await ValidateLayout(Layout))
+ return;
State.EntrySource = new LayoutEntrySource(Layout);
await Dispatcher.UIThread.InvokeAsync(SetDeviceImages, DispatcherPriority.Background);
State.ChangeScreen();
}
+ private async Task 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()
{
if (Layout == null)
return;
-
+
MemoryStream deviceWithoutLeds = new();
MemoryStream deviceWithLeds = new();
-
+
using (RenderTargetBitmap image = Layout.RenderLayout(false))
{
image.Save(deviceWithoutLeds);
deviceWithoutLeds.Seek(0, SeekOrigin.Begin);
}
+
using (RenderTargetBitmap image = Layout.RenderLayout(true))
{
image.Save(deviceWithLeds);
@@ -116,7 +169,7 @@ public partial class LayoutSelectionStepViewModel : SubmissionViewModel
foreach (Stream stateImage in State.Images)
stateImage.Dispose();
State.Images.Clear();
-
+
// Go through the hassle of resizing the image to 128x128 without losing aspect ratio, padding is added for this
State.Icon = ResizeImage(deviceWithoutLeds, 128);
State.Images.Add(deviceWithoutLeds);
@@ -126,11 +179,11 @@ public partial class LayoutSelectionStepViewModel : SubmissionViewModel
private Stream ResizeImage(Stream image, int size)
{
MemoryStream output = new();
- using MemoryStream input = new();
-
+ using MemoryStream input = new();
+
image.CopyTo(input);
input.Seek(0, SeekOrigin.Begin);
-
+
using SKBitmap? sourceBitmap = SKBitmap.Decode(input);
int sourceWidth = sourceBitmap.Width;
int sourceHeight = sourceBitmap.Height;
diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/UploadStepViewModel.cs b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/UploadStepViewModel.cs
index c797bb1d8..89814a7fa 100644
--- a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/UploadStepViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/UploadStepViewModel.cs
@@ -1,4 +1,5 @@
using System;
+using System.IO;
using System.Linq;
using System.Reactive.Disposables;
using System.Reactive.Linq;
@@ -131,12 +132,34 @@ public partial class UploadStepViewModel : SubmissionViewModel
State.ChangeScreen();
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();
+ return null;
+ }
+ }
if (State.Icon == null)
return entryId;
- // Upload image
+ // Upload icon
try
{
ImageUploadResult imageUploadResult = await _workshopService.SetEntryIcon(entryId.Value, _progress, State.Icon, cancellationToken);
@@ -146,12 +169,7 @@ public partial class UploadStepViewModel : SubmissionViewModel
catch (Exception e)
{
// It's not critical if this fails
- 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
- );
+ 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);
}
return entryId;
diff --git a/src/Artemis.WebClient.Workshop/Artemis.WebClient.Workshop.csproj b/src/Artemis.WebClient.Workshop/Artemis.WebClient.Workshop.csproj
index 15fc63b9d..0f6acb5f9 100644
--- a/src/Artemis.WebClient.Workshop/Artemis.WebClient.Workshop.csproj
+++ b/src/Artemis.WebClient.Workshop/Artemis.WebClient.Workshop.csproj
@@ -53,4 +53,10 @@
+
+
+
+ ..\..\..\RGB.NET\bin\net7.0\RGB.NET.Layout.dll
+
+
diff --git a/src/Artemis.WebClient.Workshop/Artemis.WebClient.Workshop.csproj.DotSettings b/src/Artemis.WebClient.Workshop/Artemis.WebClient.Workshop.csproj.DotSettings
new file mode 100644
index 000000000..ebe5d57a0
--- /dev/null
+++ b/src/Artemis.WebClient.Workshop/Artemis.WebClient.Workshop.csproj.DotSettings
@@ -0,0 +1,4 @@
+
+ True
+ True
+ True
\ No newline at end of file
diff --git a/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/EntryInstallationHandlerFactory.cs b/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/EntryInstallationHandlerFactory.cs
index ec9c63566..9153d7c5a 100644
--- a/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/EntryInstallationHandlerFactory.cs
+++ b/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/EntryInstallationHandlerFactory.cs
@@ -1,5 +1,4 @@
-using Artemis.WebClient.Workshop.Handlers.InstallationHandlers.Implementations;
-using DryIoc;
+using DryIoc;
namespace Artemis.WebClient.Workshop.Handlers.InstallationHandlers;
diff --git a/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/ProfileEntryInstallationHandler.cs b/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/ProfileEntryInstallationHandler.cs
index f3473f66a..8b7f33e73 100644
--- a/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/ProfileEntryInstallationHandler.cs
+++ b/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/ProfileEntryInstallationHandler.cs
@@ -4,7 +4,7 @@ using Artemis.UI.Shared.Extensions;
using Artemis.UI.Shared.Utilities;
using Artemis.WebClient.Workshop.Services;
-namespace Artemis.WebClient.Workshop.Handlers.InstallationHandlers.Implementations;
+namespace Artemis.WebClient.Workshop.Handlers.InstallationHandlers;
public class ProfileEntryInstallationHandler : IEntryInstallationHandler
{
diff --git a/src/Artemis.WebClient.Workshop/Handlers/UploadHandlers/Implementations/LayoutEntryUploadHandler.cs b/src/Artemis.WebClient.Workshop/Handlers/UploadHandlers/Implementations/LayoutEntryUploadHandler.cs
index 90f9869c5..d61a63472 100644
--- a/src/Artemis.WebClient.Workshop/Handlers/UploadHandlers/Implementations/LayoutEntryUploadHandler.cs
+++ b/src/Artemis.WebClient.Workshop/Handlers/UploadHandlers/Implementations/LayoutEntryUploadHandler.cs
@@ -1,5 +1,7 @@
-using System.Xml.Serialization;
+using System.IO.Compression;
+using Artemis.Core;
using Artemis.UI.Shared.Utilities;
+using Artemis.WebClient.Workshop.Exceptions;
using RGB.NET.Layout;
namespace Artemis.WebClient.Workshop.Handlers.UploadHandlers;
@@ -11,15 +13,56 @@ public class LayoutEntryUploadHandler : IEntryUploadHandler
{
if (entrySource is not LayoutEntrySource source)
throw new InvalidOperationException("Can only create releases for layouts");
-
- // Create a copy of the layout, image paths are about to be rewritten
- XmlSerializer serializer = new(typeof(DeviceLayout));
- using MemoryStream ms = new();
- await using StreamWriter writer = new(ms);
- serializer.Serialize(writer, source.Layout.RgbLayout);
- await writer.FlushAsync();
- ms.Seek(0, SeekOrigin.Begin);
+
+ using MemoryStream archiveStream = new();
+ using MemoryStream layoutStream = new();
+
+ source.Layout.RgbLayout.Save(layoutStream);
+ layoutStream.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();
}
+
+ private static void CopyImage(string layoutPath, string? imagePath, ZipArchive archive)
+ {
+ if (imagePath == null)
+ return;
+
+ string fullPath = Path.Combine(layoutPath, imagePath);
+ archive.CreateEntryFromFile(fullPath, imagePath);
+ }
}
\ 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 3f2303c00..57553b4ac 100644
--- a/src/Artemis.WebClient.Workshop/Services/Interfaces/IWorkshopService.cs
+++ b/src/Artemis.WebClient.Workshop/Services/Interfaces/IWorkshopService.cs
@@ -7,6 +7,7 @@ public interface IWorkshopService
{
Task GetEntryIcon(long entryId, CancellationToken cancellationToken);
Task SetEntryIcon(long entryId, Progress progress, Stream icon, CancellationToken cancellationToken);
+ Task UploadEntryImage(long entryId, Progress progress, Stream image, CancellationToken cancellationToken);
Task GetWorkshopStatus(CancellationToken cancellationToken);
Task ValidateWorkshopStatus(CancellationToken cancellationToken);
Task NavigateToEntry(long entryId, EntryType entryType);
diff --git a/src/Artemis.WebClient.Workshop/Services/WorkshopService.cs b/src/Artemis.WebClient.Workshop/Services/WorkshopService.cs
index f40a15375..c2b343981 100644
--- a/src/Artemis.WebClient.Workshop/Services/WorkshopService.cs
+++ b/src/Artemis.WebClient.Workshop/Services/WorkshopService.cs
@@ -56,6 +56,27 @@ public class WorkshopService : IWorkshopService
return ImageUploadResult.FromSuccess();
}
+ ///
+ public async Task UploadEntryImage(long entryId, Progress 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();
+ }
+
///
public async Task GetWorkshopStatus(CancellationToken cancellationToken)
{