Images WIP

This commit is contained in:
RobertBeekman committed 2023-11-05 21:27:41 +01:00
1 parent 6b4e84c95a
commit 0667d58ed8
56 files changed
+783 -454

No files matched your search

@@ -15,7 +15,7 @@
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
<PackageReference Include="ReactiveUI" Version="19.5.1" />
<PackageReference Include="StrawberryShake.Server" Version="13.6.0-preview.31" />
<PackageReference Include="StrawberryShake.Server" Version="13.7.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.0.3" />
<PackageReference Include="System.Reactive" Version="6.0.0" />
</ItemGroup>
@@ -16,6 +16,7 @@ public class EntryInstallationHandlerFactory
return entryType switch
{
EntryType.Profile => _container.Resolve<ProfileEntryInstallationHandler>(),
EntryType.Layout => _container.Resolve<LayoutEntryInstallationHandler>(),
_ => throw new NotSupportedException($"EntryType '{entryType}' is not supported.")
};
}
@@ -0,0 +1,45 @@
using Artemis.UI.Shared.Extensions;
using Artemis.UI.Shared.Utilities;
using Artemis.WebClient.Workshop.Services;
namespace Artemis.WebClient.Workshop.Handlers.InstallationHandlers;
public class LayoutEntryInstallationHandler : IEntryInstallationHandler
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly IWorkshopService _workshopService;
public LayoutEntryInstallationHandler(IHttpClientFactory httpClientFactory, IWorkshopService workshopService)
{
_httpClientFactory = httpClientFactory;
_workshopService = workshopService;
}
public async Task<EntryInstallResult> InstallAsync(IGetEntryById_Entry entry, long releaseId, Progress<StreamProgress> progress, CancellationToken cancellationToken)
{
throw new NotImplementedException();
using MemoryStream stream = new();
// Download the provided release
try
{
HttpClient client = _httpClientFactory.CreateClient(WorkshopConstants.WORKSHOP_CLIENT_NAME);
await client.DownloadDataAsync($"releases/download/{releaseId}", stream, progress, cancellationToken);
}
catch (Exception e)
{
return EntryInstallResult.FromFailure(e.Message);
}
// return EntryInstallResult.FromSuccess();
}
public async Task<EntryUninstallResult> UninstallAsync(InstalledEntry installedEntry, CancellationToken cancellationToken)
{
throw new NotImplementedException();
if (!Guid.TryParse(installedEntry.LocalReference, out Guid profileId))
return EntryUninstallResult.FromFailure("Local reference does not contain a GUID");
}
}
@@ -0,0 +1,16 @@
namespace Artemis.WebClient.Workshop.Handlers.UploadHandlers;
public class ImageUploadRequest
{
public ImageUploadRequest(Stream file, string name, string? description)
{
File = file;
Name = name.Length > 50 ? name.Substring(0, 50) : name;
if (description != null)
Description = description.Length > 150 ? description.Substring(0, 150) : description;
}
public Stream File { get; set; }
public string Name { get; set; }
public string? Description { get; set; }
}
@@ -1,13 +1,23 @@
using System.IO.Compression;
using System.Net.Http.Headers;
using Artemis.Core;
using Artemis.UI.Shared.Utilities;
using Artemis.WebClient.Workshop.Entities;
using Artemis.WebClient.Workshop.Exceptions;
using Newtonsoft.Json;
using RGB.NET.Layout;
namespace Artemis.WebClient.Workshop.Handlers.UploadHandlers;
public class LayoutEntryUploadHandler : IEntryUploadHandler
{
private readonly IHttpClientFactory _httpClientFactory;
public LayoutEntryUploadHandler(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
/// <inheritdoc />
public async Task<EntryUploadResult> CreateReleaseAsync(long entryId, IEntrySource entrySource, Progress<StreamProgress> progress, CancellationToken cancellationToken)
{
@@ -46,15 +56,28 @@ public class LayoutEntryUploadHandler : IEntryUploadHandler
}
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))
await using (FileStream fileStream = new(@"C:\Users\Robert\Desktop\layout-test.zip", FileMode.OpenOrCreate))
{
archiveStream.WriteTo(fileStream);
await archiveStream.CopyToAsync(fileStream, cancellationToken);
}
archiveStream.Seek(0, SeekOrigin.Begin);
// Submit the archive
HttpClient client = _httpClientFactory.CreateClient(WorkshopConstants.WORKSHOP_CLIENT_NAME);
return new EntryUploadResult();
// Construct the request
MultipartFormDataContent content = new();
ProgressableStreamContent streamContent = new(archiveStream, progress);
streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/zip");
content.Add(streamContent, "file", "file.zip");
// Submit
HttpResponseMessage response = await client.PostAsync("releases/upload/" + entryId, content, cancellationToken);
if (!response.IsSuccessStatusCode)
return EntryUploadResult.FromFailure($"{response.StatusCode} - {await response.Content.ReadAsStringAsync(cancellationToken)}");
Release? release = JsonConvert.DeserializeObject<Release>(await response.Content.ReadAsStringAsync(cancellationToken));
return release != null ? EntryUploadResult.FromSuccess(release) : EntryUploadResult.FromFailure("Failed to deserialize response");
}
private static void CopyImage(string layoutPath, string? imagePath, ZipArchive archive)
@@ -3,6 +3,12 @@ fragment category on Category {
icon
}
fragment image on Image {
id
name
description
}
fragment layoutInfo on LayoutInfo {
id
deviceProvider
@@ -20,4 +26,28 @@ fragment submittedEntry on Entry {
entryType
downloads
createdAt
}
fragment entryDetails on Entry {
id
author
name
summary
entryType
downloads
createdAt
description
categories {
...category
}
latestRelease {
id
version
downloadSize
md5Hash
createdAt
}
images {
...image
}
}
@@ -1,22 +1,5 @@
query GetEntryById($id: Long!) {
entry(id: $id) {
id
author
name
summary
entryType
downloads
createdAt
description
categories {
...category
}
latestRelease {
id
version
downloadSize
md5Hash
createdAt
}
...entryDetails
}
}
@@ -7,7 +7,7 @@ public interface IWorkshopService
{
Task<Stream?> GetEntryIcon(long entryId, 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<ImageUploadResult> UploadEntryImage(long entryId, ImageUploadRequest request, Progress<StreamProgress> progress, CancellationToken cancellationToken);
Task<WorkshopStatus> GetWorkshopStatus(CancellationToken cancellationToken);
Task<bool> ValidateWorkshopStatus(CancellationToken cancellationToken);
Task NavigateToEntry(long entryId, EntryType entryType);
@@ -57,19 +57,19 @@ public class WorkshopService : IWorkshopService
}
/// <inheritdoc />
public async Task<ImageUploadResult> UploadEntryImage(long entryId, Progress<StreamProgress> progress, Stream image, CancellationToken cancellationToken)
public async Task<ImageUploadResult> UploadEntryImage(long entryId, ImageUploadRequest request, Progress<StreamProgress> progress, CancellationToken cancellationToken)
{
image.Seek(0, SeekOrigin.Begin);
request.File.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);
ProgressableStreamContent streamContent = new(request.File, 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)
@@ -5,6 +5,8 @@ schema {
mutation: Mutation
}
directive @tag(name: String!) on SCHEMA | SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | INTERFACE | UNION | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
type Category {
icon: String!
id: Long!
@@ -50,8 +52,13 @@ type Entry {
}
type Image {
description: String
height: Int!
id: UUID!
mimeType: String!
name: String!
size: Long!
width: Int!
}
type LayoutInfo {
@@ -252,14 +259,39 @@ input EntryTypeOperationFilterInput {
input ImageFilterInput {
and: [ImageFilterInput!]
description: StringOperationFilterInput
height: IntOperationFilterInput
id: UuidOperationFilterInput
mimeType: StringOperationFilterInput
name: StringOperationFilterInput
or: [ImageFilterInput!]
size: LongOperationFilterInput
width: IntOperationFilterInput
}
input ImageSortInput {
description: SortEnumType
height: SortEnumType
id: SortEnumType
mimeType: SortEnumType
name: SortEnumType
size: SortEnumType
width: SortEnumType
}
input IntOperationFilterInput {
eq: Int
gt: Int
gte: Int
in: [Int]
lt: Int
lte: Int
neq: Int
ngt: Int
ngte: Int
nin: [Int]
nlt: Int
nlte: Int
}
input LayoutInfoFilterInput {