diff --git a/src/Artemis.UI/Artemis.UI.csproj b/src/Artemis.UI/Artemis.UI.csproj
index e4a63012a..71d09529d 100644
--- a/src/Artemis.UI/Artemis.UI.csproj
+++ b/src/Artemis.UI/Artemis.UI.csproj
@@ -58,4 +58,19 @@
..\..\..\RGB.NET\bin\net7.0\RGB.NET.Layout.dll
+
+
+
+ EntryListInputView.axaml
+ Code
+
+
+ EntryListItemView.axaml
+ Code
+
+
+ EntrySpecificationsView.axaml
+ Code
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Debugger/DebugViewModel.cs b/src/Artemis.UI/Screens/Debugger/DebugViewModel.cs
index 6ccd0cbb8..8cc677053 100644
--- a/src/Artemis.UI/Screens/Debugger/DebugViewModel.cs
+++ b/src/Artemis.UI/Screens/Debugger/DebugViewModel.cs
@@ -6,6 +6,7 @@ using Artemis.UI.Screens.Debugger.Logs;
using Artemis.UI.Screens.Debugger.Performance;
using Artemis.UI.Screens.Debugger.Render;
using Artemis.UI.Screens.Debugger.Routing;
+using Artemis.UI.Screens.Debugger.Workshop;
using Artemis.UI.Services.Interfaces;
using Artemis.UI.Shared;
using PropertyChanged.SourceGenerator;
@@ -17,9 +18,9 @@ public partial class DebugViewModel : ActivatableViewModelBase, IScreen
{
[Notify] private ViewModelBase _selectedItem;
- public DebugViewModel(IDebugService debugService, RenderDebugViewModel render, DataModelDebugViewModel dataModel, PerformanceDebugViewModel performance, RoutingDebugViewModel routing, LogsDebugViewModel logs)
+ public DebugViewModel(IDebugService debugService, RenderDebugViewModel render, DataModelDebugViewModel dataModel, PerformanceDebugViewModel performance, RoutingDebugViewModel routing, WorkshopDebugViewModel workshop, LogsDebugViewModel logs)
{
- Items = new ObservableCollection {render, dataModel, performance, routing, logs};
+ Items = new ObservableCollection {render, dataModel, performance, routing, workshop, logs};
_selectedItem = render;
this.WhenActivated(d => Disposable.Create(debugService.ClearDebugger).DisposeWith(d));
diff --git a/src/Artemis.UI/Screens/Debugger/Tabs/Workshop/WorkshopDebugView.axaml b/src/Artemis.UI/Screens/Debugger/Tabs/Workshop/WorkshopDebugView.axaml
new file mode 100644
index 000000000..647d30f06
--- /dev/null
+++ b/src/Artemis.UI/Screens/Debugger/Tabs/Workshop/WorkshopDebugView.axaml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Debugger/Tabs/Workshop/WorkshopDebugView.axaml.cs b/src/Artemis.UI/Screens/Debugger/Tabs/Workshop/WorkshopDebugView.axaml.cs
new file mode 100644
index 000000000..832b85df8
--- /dev/null
+++ b/src/Artemis.UI/Screens/Debugger/Tabs/Workshop/WorkshopDebugView.axaml.cs
@@ -0,0 +1,14 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using Avalonia.ReactiveUI;
+
+namespace Artemis.UI.Screens.Debugger.Workshop;
+
+public partial class WorkshopDebugView : ReactiveUserControl
+{
+ public WorkshopDebugView()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Debugger/Tabs/Workshop/WorkshopDebugViewModel.cs b/src/Artemis.UI/Screens/Debugger/Tabs/Workshop/WorkshopDebugViewModel.cs
new file mode 100644
index 000000000..26a1e031f
--- /dev/null
+++ b/src/Artemis.UI/Screens/Debugger/Tabs/Workshop/WorkshopDebugViewModel.cs
@@ -0,0 +1,30 @@
+using System.Threading;
+using Artemis.UI.Extensions;
+using Artemis.UI.Shared;
+using Artemis.WebClient.Workshop.Services;
+using Newtonsoft.Json;
+using PropertyChanged.SourceGenerator;
+
+namespace Artemis.UI.Screens.Debugger.Workshop;
+
+public partial class WorkshopDebugViewModel : ActivatableViewModelBase
+{
+
+ [Notify] private string? _token;
+ [Notify] private bool _emailVerified;
+ [Notify] private string? _claims;
+ [Notify] private IWorkshopService.WorkshopStatus? _workshopStatus;
+
+ public WorkshopDebugViewModel(IWorkshopService workshopService, IAuthenticationService authenticationService)
+ {
+ DisplayName = "Workshop";
+
+ this.WhenActivatedAsync(async _ =>
+ {
+ Token = await authenticationService.GetBearer();
+ EmailVerified = authenticationService.GetIsEmailVerified();
+ Claims = JsonConvert.SerializeObject(authenticationService.Claims, Formatting.Indented);
+ WorkshopStatus = await workshopService.GetWorkshopStatus(CancellationToken.None);
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImageView.axaml b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImageView.axaml
new file mode 100644
index 000000000..4dd4f029f
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImageView.axaml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImageView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImageView.axaml.cs
new file mode 100644
index 000000000..c7759c110
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImageView.axaml.cs
@@ -0,0 +1,13 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace Artemis.UI.Screens.Workshop.Entries.Details;
+
+public partial class EntryImageView : UserControl
+{
+ public EntryImageView()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImageViewModel.cs b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImageViewModel.cs
new file mode 100644
index 000000000..5bfaa1052
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImageViewModel.cs
@@ -0,0 +1,17 @@
+using Artemis.WebClient.Workshop;
+
+namespace Artemis.UI.Screens.Workshop.Entries.Details;
+
+public class EntryImageViewModel
+{
+ public EntryImageViewModel(IImage image)
+ {
+ Image = image;
+ Url = $"{WorkshopConstants.WORKSHOP_URL}/images/{image.Id}.png";
+ ThumbnailUrl = $"{WorkshopConstants.WORKSHOP_URL}/images/{image.Id}-thumb.png";
+ }
+
+ public IImage Image { get; }
+ public string Url { get; }
+ public string ThumbnailUrl { get; }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImagesView.axaml b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImagesView.axaml
new file mode 100644
index 000000000..3c7168cde
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImagesView.axaml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImagesView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImagesView.axaml.cs
new file mode 100644
index 000000000..0d0672f94
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImagesView.axaml.cs
@@ -0,0 +1,13 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace Artemis.UI.Screens.Workshop.Entries.Details;
+
+public partial class EntryImagesView : UserControl
+{
+ public EntryImagesView()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImagesViewModel.cs b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImagesViewModel.cs
new file mode 100644
index 000000000..c38cbb744
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryImagesViewModel.cs
@@ -0,0 +1,16 @@
+using System.Collections.ObjectModel;
+using System.Linq;
+using Artemis.UI.Shared;
+using Artemis.WebClient.Workshop;
+
+namespace Artemis.UI.Screens.Workshop.Entries.Details;
+
+public class EntryImagesViewModel : ViewModelBase
+{
+ public ObservableCollection Images { get; }
+
+ public EntryImagesViewModel(IEntryDetails entryDetails)
+ {
+ Images = new ObservableCollection(entryDetails.Images.Select(i => new EntryImageViewModel(i)));
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryInfoView.axaml b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryInfoView.axaml
new file mode 100644
index 000000000..2d0ecbffa
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryInfoView.axaml
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ downloads
+
+
+
+
+ Created
+
+
+
+
+ Updated
+
+
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryInfoView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryInfoView.axaml.cs
new file mode 100644
index 000000000..5c6ec0a0c
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryInfoView.axaml.cs
@@ -0,0 +1,13 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace Artemis.UI.Screens.Workshop.Entries.Details;
+
+public partial class EntryInfoView : UserControl
+{
+ public EntryInfoView()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryInfoViewModel.cs b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryInfoViewModel.cs
new file mode 100644
index 000000000..24bb74225
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryInfoViewModel.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Threading.Tasks;
+using Artemis.Core;
+using Artemis.UI.Shared;
+using Artemis.UI.Shared.Services;
+using Artemis.WebClient.Workshop;
+
+namespace Artemis.UI.Screens.Workshop.Entries.Details;
+
+public class EntryInfoViewModel : ViewModelBase
+{
+ private readonly INotificationService _notificationService;
+ public IGetEntryById_Entry Entry { get; }
+ public DateTimeOffset? UpdatedAt { get; }
+
+ public EntryInfoViewModel(IGetEntryById_Entry entry, INotificationService notificationService)
+ {
+ _notificationService = notificationService;
+ Entry = entry;
+ UpdatedAt = Entry.LatestRelease?.CreatedAt ?? Entry.CreatedAt;
+ }
+
+ public async Task CopyShareLink()
+ {
+ await Shared.UI.Clipboard.SetTextAsync($"{WorkshopConstants.WORKSHOP_URL}/entries/{Entry.Id}/{StringUtilities.UrlFriendly(Entry.Name)}");
+ _notificationService.CreateNotification().WithTitle("Copied share link to clipboard.").Show();
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryReleasesView.axaml b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryReleasesView.axaml
new file mode 100644
index 000000000..78944e621
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryReleasesView.axaml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+ Latest release
+
+
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryReleasesView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryReleasesView.axaml.cs
new file mode 100644
index 000000000..523edc7e5
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryReleasesView.axaml.cs
@@ -0,0 +1,13 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace Artemis.UI.Screens.Workshop.Entries.Details;
+
+public partial class EntryReleasesView : UserControl
+{
+ public EntryReleasesView()
+ {
+ InitializeComponent();
+ }
+}
\ 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
new file mode 100644
index 000000000..11608288a
--- /dev/null
+++ b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryReleasesViewModel.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Reactive;
+using System.Threading;
+using System.Threading.Tasks;
+using Artemis.UI.Shared;
+using Artemis.UI.Shared.Services;
+using Artemis.UI.Shared.Services.Builders;
+using Artemis.UI.Shared.Utilities;
+using Artemis.WebClient.Workshop;
+using Artemis.WebClient.Workshop.Handlers.InstallationHandlers;
+using Humanizer;
+using ReactiveUI;
+
+namespace Artemis.UI.Screens.Workshop.Entries.Details;
+
+public class EntryReleasesViewModel : ViewModelBase
+{
+ private readonly EntryInstallationHandlerFactory _factory;
+ private readonly IWindowService _windowService;
+ private readonly INotificationService _notificationService;
+
+ public EntryReleasesViewModel(IGetEntryById_Entry entry, EntryInstallationHandlerFactory factory, IWindowService windowService, INotificationService notificationService)
+ {
+ _factory = factory;
+ _windowService = windowService;
+ _notificationService = notificationService;
+
+ Entry = entry;
+ DownloadLatestRelease = ReactiveCommand.CreateFromTask(ExecuteDownloadLatestRelease);
+ }
+
+ public IGetEntryById_Entry Entry { get; }
+ public ReactiveCommand DownloadLatestRelease { get; }
+
+ private async Task ExecuteDownloadLatestRelease(CancellationToken cancellationToken)
+ {
+ if (Entry.LatestRelease == null)
+ return;
+
+ bool confirm = await _windowService.ShowConfirmContentDialog(
+ "Install latest release",
+ $"Are you sure you want to download and install version {Entry.LatestRelease.Version} of {Entry.Name}?"
+ );
+ if (!confirm)
+ return;
+
+ IEntryInstallationHandler installationHandler = _factory.CreateHandler(Entry.EntryType);
+ EntryInstallResult result = await installationHandler.InstallAsync(Entry, Entry.LatestRelease.Id, new Progress(), cancellationToken);
+ if (result.IsSuccess)
+ _notificationService.CreateNotification().WithTitle($"{Entry.EntryType.Humanize(LetterCasing.Sentence)} installed").WithSeverity(NotificationSeverity.Success).Show();
+ else
+ {
+ _notificationService.CreateNotification()
+ .WithTitle($"Failed to install {Entry.EntryType.Humanize(LetterCasing.LowerCase)}")
+ .WithMessage(result.Message)
+ .WithSeverity(NotificationSeverity.Error).Show();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/EntrySpecificationsView.axaml b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntrySpecificationsView.axaml
similarity index 97%
rename from src/Artemis.UI/Screens/Workshop/Entries/EntrySpecificationsView.axaml
rename to src/Artemis.UI/Screens/Workshop/Entries/Details/EntrySpecificationsView.axaml
index 94bf49bb5..8b7fb56e6 100644
--- a/src/Artemis.UI/Screens/Workshop/Entries/EntrySpecificationsView.axaml
+++ b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntrySpecificationsView.axaml
@@ -9,9 +9,10 @@
xmlns:avaloniaEdit="https://github.com/avaloniaui/avaloniaedit"
xmlns:mdxaml="https://github.com/whistyun/Markdown.Avalonia.Tight"
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
+ xmlns:details="clr-namespace:Artemis.UI.Screens.Workshop.Entries.Details"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="800"
- x:Class="Artemis.UI.Screens.Workshop.Entries.EntrySpecificationsView"
- x:DataType="entries:EntrySpecificationsViewModel">
+ x:Class="Artemis.UI.Screens.Workshop.Entries.Details.EntrySpecificationsView"
+ x:DataType="details:EntrySpecificationsViewModel">
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/EntrySpecificationsView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntrySpecificationsView.axaml.cs
similarity index 87%
rename from src/Artemis.UI/Screens/Workshop/Entries/EntrySpecificationsView.axaml.cs
rename to src/Artemis.UI/Screens/Workshop/Entries/Details/EntrySpecificationsView.axaml.cs
index b3289a3bf..37c85d118 100644
--- a/src/Artemis.UI/Screens/Workshop/Entries/EntrySpecificationsView.axaml.cs
+++ b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntrySpecificationsView.axaml.cs
@@ -1,5 +1,4 @@
using System.Linq;
-using Artemis.UI.Shared.Extensions;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
@@ -8,8 +7,9 @@ using Avalonia.ReactiveUI;
using AvaloniaEdit.TextMate;
using ReactiveUI;
using TextMateSharp.Grammars;
+using VisualExtensions = Artemis.UI.Shared.Extensions.VisualExtensions;
-namespace Artemis.UI.Screens.Workshop.Entries;
+namespace Artemis.UI.Screens.Workshop.Entries.Details;
public partial class EntrySpecificationsView : ReactiveUserControl
{
@@ -23,7 +23,7 @@ public partial class EntrySpecificationsView : ReactiveUserControl().FirstOrDefault();
- _previewScrollViewer = DescriptionPreview.GetVisualChildrenOfType().FirstOrDefault();
+ _editorScrollViewer = VisualExtensions.GetVisualChildrenOfType(DescriptionEditor).FirstOrDefault();
+ _previewScrollViewer = VisualExtensions.GetVisualChildrenOfType(DescriptionPreview).FirstOrDefault();
if (_editorScrollViewer != null)
_editorScrollViewer.PropertyChanged += EditorScrollViewerOnPropertyChanged;
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/EntrySpecificationsViewModel.cs b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntrySpecificationsViewModel.cs
similarity index 87%
rename from src/Artemis.UI/Screens/Workshop/Entries/EntrySpecificationsViewModel.cs
rename to src/Artemis.UI/Screens/Workshop/Entries/Details/EntrySpecificationsViewModel.cs
index af6a2894e..f06262a3f 100644
--- a/src/Artemis.UI/Screens/Workshop/Entries/EntrySpecificationsViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntrySpecificationsViewModel.cs
@@ -22,7 +22,7 @@ using ReactiveUI.Validation.Extensions;
using ReactiveUI.Validation.Helpers;
using StrawberryShake;
-namespace Artemis.UI.Screens.Workshop.Entries;
+namespace Artemis.UI.Screens.Workshop.Entries.Details;
public partial class EntrySpecificationsViewModel : ValidatableViewModelBase
{
@@ -52,12 +52,12 @@ public partial class EntrySpecificationsViewModel : ValidatableViewModelBase
.Subscribe();
SelectedCategories = selectedCategories;
- this.ValidationRule(vm => vm.Name, s => !string.IsNullOrWhiteSpace(s), "Name is required");
- this.ValidationRule(vm => vm.Summary, s => !string.IsNullOrWhiteSpace(s), "Summary is required");
- ValidationHelper descriptionRule = this.ValidationRule(vm => vm.Description, s => !string.IsNullOrWhiteSpace(s), "Description is required");
+ this.ValidationRule(vm => vm.Name, s => !string.IsNullOrWhiteSpace(s), "Name is required");
+ this.ValidationRule(vm => vm.Summary, s => !string.IsNullOrWhiteSpace(s), "Summary is required");
+ ValidationHelper descriptionRule = this.ValidationRule(vm => vm.Description, s => !string.IsNullOrWhiteSpace(s), "Description is required");
// These don't use inputs that support validation messages, do so manually
- ValidationHelper iconRule = this.ValidationRule(vm => vm.IconBitmap, s => s != null, "Icon required");
+ ValidationHelper iconRule = this.ValidationRule(vm => vm.IconBitmap, s => s != null, "Icon required");
ValidationHelper categoriesRule = this.ValidationRule(vm => vm.Categories, Categories.ToObservableChangeSet().AutoRefresh(c => c.IsSelected).Filter(c => c.IsSelected).IsNotEmpty(),
"At least one category must be selected"
);
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/EntryInstallationDialogView.axaml b/src/Artemis.UI/Screens/Workshop/Entries/EntryInstallationDialogView.axaml
deleted file mode 100644
index af75201c2..000000000
--- a/src/Artemis.UI/Screens/Workshop/Entries/EntryInstallationDialogView.axaml
+++ /dev/null
@@ -1,8 +0,0 @@
-
- Welcome to Avalonia!
-
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/EntryInstallationDialogView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Entries/EntryInstallationDialogView.axaml.cs
deleted file mode 100644
index b228f4b0f..000000000
--- a/src/Artemis.UI/Screens/Workshop/Entries/EntryInstallationDialogView.axaml.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using Avalonia;
-using Avalonia.Controls;
-using Avalonia.Markup.Xaml;
-
-namespace Artemis.UI.Screens.Workshop.Entries;
-
-public partial class EntryInstallationDialogView : UserControl
-{
- public EntryInstallationDialogView()
- {
- InitializeComponent();
- }
-}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/EntryInstallationDialogViewModel.cs b/src/Artemis.UI/Screens/Workshop/Entries/EntryInstallationDialogViewModel.cs
deleted file mode 100644
index 7b7cbca67..000000000
--- a/src/Artemis.UI/Screens/Workshop/Entries/EntryInstallationDialogViewModel.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using Artemis.UI.Shared;
-
-namespace Artemis.UI.Screens.Workshop.Entries;
-
-public class EntryInstallationDialogViewModel : ContentDialogViewModelBase
-{
-
-}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/EntryListInputView.axaml b/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListInputView.axaml
similarity index 90%
rename from src/Artemis.UI/Screens/Workshop/Entries/EntryListInputView.axaml
rename to src/Artemis.UI/Screens/Workshop/Entries/List/EntryListInputView.axaml
index 0b01eb0e9..d5c3b34f9 100644
--- a/src/Artemis.UI/Screens/Workshop/Entries/EntryListInputView.axaml
+++ b/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListInputView.axaml
@@ -4,9 +4,10 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:entries="clr-namespace:Artemis.UI.Screens.Workshop.Entries"
xmlns:system="clr-namespace:System;assembly=System.Runtime"
+ xmlns:list="clr-namespace:Artemis.UI.Screens.Workshop.Entries.List"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
- x:Class="Artemis.UI.Screens.Workshop.Entries.EntryListInputView"
- x:DataType="entries:EntryListInputViewModel">
+ x:Class="Artemis.UI.Screens.Workshop.Entries.List.EntryListInputView"
+ x:DataType="list:EntryListInputViewModel">
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/EntryListInputView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListInputView.axaml.cs
similarity index 63%
rename from src/Artemis.UI/Screens/Workshop/Entries/EntryListInputView.axaml.cs
rename to src/Artemis.UI/Screens/Workshop/Entries/List/EntryListInputView.axaml.cs
index 650d82eb9..3d2661690 100644
--- a/src/Artemis.UI/Screens/Workshop/Entries/EntryListInputView.axaml.cs
+++ b/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListInputView.axaml.cs
@@ -1,8 +1,6 @@
-using Avalonia;
using Avalonia.Controls;
-using Avalonia.Markup.Xaml;
-namespace Artemis.UI.Screens.Workshop.Entries;
+namespace Artemis.UI.Screens.Workshop.Entries.List;
public partial class EntryListInputView : UserControl
{
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/EntryListInputViewModel.cs b/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListInputViewModel.cs
similarity index 96%
rename from src/Artemis.UI/Screens/Workshop/Entries/EntryListInputViewModel.cs
rename to src/Artemis.UI/Screens/Workshop/Entries/List/EntryListInputViewModel.cs
index f9f72c092..f34348a8b 100644
--- a/src/Artemis.UI/Screens/Workshop/Entries/EntryListInputViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListInputViewModel.cs
@@ -4,7 +4,7 @@ using Artemis.UI.Shared;
using PropertyChanged.SourceGenerator;
using ReactiveUI;
-namespace Artemis.UI.Screens.Workshop.Entries;
+namespace Artemis.UI.Screens.Workshop.Entries.List;
public partial class EntryListInputViewModel : ViewModelBase
{
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/EntryListItemView.axaml b/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListItemView.axaml
similarity index 94%
rename from src/Artemis.UI/Screens/Workshop/Entries/EntryListItemView.axaml
rename to src/Artemis.UI/Screens/Workshop/Entries/List/EntryListItemView.axaml
index 2831515f6..f041d5655 100644
--- a/src/Artemis.UI/Screens/Workshop/Entries/EntryListItemView.axaml
+++ b/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListItemView.axaml
@@ -6,9 +6,10 @@
xmlns:entries1="clr-namespace:Artemis.UI.Screens.Workshop.Entries"
xmlns:il="clr-namespace:AsyncImageLoader;assembly=AsyncImageLoader.Avalonia"
xmlns:converters="clr-namespace:Artemis.UI.Converters"
+ xmlns:list="clr-namespace:Artemis.UI.Screens.Workshop.Entries.List"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="110"
- x:Class="Artemis.UI.Screens.Workshop.Entries.EntryListItemView"
- x:DataType="entries1:EntryListItemViewModel">
+ x:Class="Artemis.UI.Screens.Workshop.Entries.List.EntryListItemView"
+ x:DataType="list:EntryListItemViewModel">
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/EntryListItemView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListItemView.axaml.cs
similarity index 78%
rename from src/Artemis.UI/Screens/Workshop/Entries/EntryListItemView.axaml.cs
rename to src/Artemis.UI/Screens/Workshop/Entries/List/EntryListItemView.axaml.cs
index 864057476..364af9252 100644
--- a/src/Artemis.UI/Screens/Workshop/Entries/EntryListItemView.axaml.cs
+++ b/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListItemView.axaml.cs
@@ -1,6 +1,6 @@
using Avalonia.ReactiveUI;
-namespace Artemis.UI.Screens.Workshop.Entries;
+namespace Artemis.UI.Screens.Workshop.Entries.List;
public partial class EntryListItemView : ReactiveUserControl
{
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/EntryListItemViewModel.cs b/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListItemViewModel.cs
similarity index 84%
rename from src/Artemis.UI/Screens/Workshop/Entries/EntryListItemViewModel.cs
rename to src/Artemis.UI/Screens/Workshop/Entries/List/EntryListItemViewModel.cs
index 4160048ff..be57fd898 100644
--- a/src/Artemis.UI/Screens/Workshop/Entries/EntryListItemViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListItemViewModel.cs
@@ -1,17 +1,12 @@
using System;
using System.Reactive;
-using System.Reactive.Disposables;
-using System.Reactive.Linq;
-using System.Threading;
using System.Threading.Tasks;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Routing;
using Artemis.WebClient.Workshop;
-using Artemis.WebClient.Workshop.Services;
-using Avalonia.Media.Imaging;
using ReactiveUI;
-namespace Artemis.UI.Screens.Workshop.Entries;
+namespace Artemis.UI.Screens.Workshop.Entries.List;
public class EntryListItemViewModel : ActivatableViewModelBase
{
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/EntryListViewModel.cs b/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListViewModel.cs
similarity index 92%
rename from src/Artemis.UI/Screens/Workshop/Entries/EntryListViewModel.cs
rename to src/Artemis.UI/Screens/Workshop/Entries/List/EntryListViewModel.cs
index 4e11a5568..7bbd75704 100644
--- a/src/Artemis.UI/Screens/Workshop/Entries/EntryListViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListViewModel.cs
@@ -17,7 +17,7 @@ using PropertyChanged.SourceGenerator;
using ReactiveUI;
using StrawberryShake;
-namespace Artemis.UI.Screens.Workshop.Entries;
+namespace Artemis.UI.Screens.Workshop.Entries.List;
public abstract partial class EntryListViewModel : RoutableScreen
{
@@ -42,8 +42,8 @@ public abstract partial class EntryListViewModel : RoutableScreen vm.TotalPages).Select(t => t > 1).ToProperty(this, vm => vm.ShowPagination);
- _isLoading = this.WhenAnyValue(vm => vm.Page, vm => vm.LoadedPage, (p, c) => p != c).ToProperty(this, vm => vm.IsLoading);
+ _showPagination = this.WhenAnyValue(vm => vm.TotalPages).Select(t => t > 1).ToProperty(this, vm => vm.ShowPagination);
+ _isLoading = this.WhenAnyValue(vm => vm.Page, vm => vm.LoadedPage, (p, c) => p != c).ToProperty(this, vm => vm.IsLoading);
CategoriesViewModel = categoriesViewModel;
InputViewModel = entryListInputViewModel;
@@ -56,7 +56,7 @@ public abstract partial class EntryListViewModel : RoutableScreen vm.Page).Skip(1).Subscribe(p => Task.Run(() => router.Navigate($"{_route}/{p}")));
+ this.WhenAnyValue(vm => vm.Page).Skip(1).Subscribe(p => Task.Run(() => router.Navigate($"{_route}/{p}")));
this.WhenActivated(d =>
{
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Tabs/LayoutListViewModel.cs b/src/Artemis.UI/Screens/Workshop/Entries/Tabs/LayoutListViewModel.cs
index 99308bd23..f4d5237e8 100644
--- a/src/Artemis.UI/Screens/Workshop/Entries/Tabs/LayoutListViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/Entries/Tabs/LayoutListViewModel.cs
@@ -1,17 +1,18 @@
using System;
using Artemis.UI.Screens.Workshop.Categories;
+using Artemis.UI.Screens.Workshop.Entries.List;
using Artemis.UI.Shared.Routing;
using Artemis.UI.Shared.Services;
using Artemis.WebClient.Workshop;
namespace Artemis.UI.Screens.Workshop.Entries.Tabs;
-public class LayoutListViewModel : EntryListViewModel
+public class LayoutListViewModel : List.EntryListViewModel
{
public LayoutListViewModel(IWorkshopClient workshopClient,
IRouter router,
CategoriesViewModel categoriesViewModel,
- EntryListInputViewModel entryListInputViewModel,
+ List.EntryListInputViewModel entryListInputViewModel,
INotificationService notificationService,
Func getEntryListViewModel)
: base("workshop/entries/layout", workshopClient, router, categoriesViewModel, entryListInputViewModel, notificationService, getEntryListViewModel)
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Tabs/ProfileListViewModel.cs b/src/Artemis.UI/Screens/Workshop/Entries/Tabs/ProfileListViewModel.cs
index 0049fdb9e..c09a587a3 100644
--- a/src/Artemis.UI/Screens/Workshop/Entries/Tabs/ProfileListViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/Entries/Tabs/ProfileListViewModel.cs
@@ -1,17 +1,18 @@
using System;
using Artemis.UI.Screens.Workshop.Categories;
+using Artemis.UI.Screens.Workshop.Entries.List;
using Artemis.UI.Shared.Routing;
using Artemis.UI.Shared.Services;
using Artemis.WebClient.Workshop;
namespace Artemis.UI.Screens.Workshop.Entries.Tabs;
-public class ProfileListViewModel : EntryListViewModel
+public class ProfileListViewModel : List.EntryListViewModel
{
public ProfileListViewModel(IWorkshopClient workshopClient,
IRouter router,
CategoriesViewModel categoriesViewModel,
- EntryListInputViewModel entryListInputViewModel,
+ List.EntryListInputViewModel entryListInputViewModel,
INotificationService notificationService,
Func getEntryListViewModel)
: base("workshop/entries/profiles", workshopClient, router, categoriesViewModel, entryListInputViewModel, notificationService, getEntryListViewModel)
diff --git a/src/Artemis.UI/Screens/Workshop/Image/ImageSubmissionView.axaml b/src/Artemis.UI/Screens/Workshop/Image/ImageSubmissionView.axaml
index ca6ec4cdd..c6b6db1d5 100644
--- a/src/Artemis.UI/Screens/Workshop/Image/ImageSubmissionView.axaml
+++ b/src/Artemis.UI/Screens/Workshop/Image/ImageSubmissionView.axaml
@@ -13,29 +13,34 @@
-
-
+
+ Source="{CompiledBinding Bitmap}" />
-
-
-
-
-
+
+
+
+
+ -
+
-
-
+
+
+
+
+
+
-
+
\ 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 c431bc2cf..c2928781a 100644
--- a/src/Artemis.UI/Screens/Workshop/Image/ImageSubmissionViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/Image/ImageSubmissionViewModel.cs
@@ -2,39 +2,43 @@ using System.IO;
using System.Reactive.Disposables;
using System.Windows.Input;
using Artemis.UI.Shared;
+using Artemis.WebClient.Workshop.Handlers.UploadHandlers;
using Avalonia.Media.Imaging;
using Avalonia.Threading;
using PropertyChanged.SourceGenerator;
using ReactiveUI;
+using ReactiveUI.Validation.Extensions;
namespace Artemis.UI.Screens.Workshop.Image;
-public partial class ImageSubmissionViewModel : ActivatableViewModelBase
+public partial class ImageSubmissionViewModel : ValidatableViewModelBase
{
[Notify(Setter.Private)] private Bitmap? _bitmap;
- [Notify(Setter.Private)] private string? _fileName;
[Notify(Setter.Private)] private string? _imageDimensions;
[Notify(Setter.Private)] private long _fileSize;
+ [Notify] private string? _name;
+ [Notify] private string? _description;
[Notify] private ICommand? _remove;
- public ImageSubmissionViewModel(Stream imageStream)
+ public ImageSubmissionViewModel(ImageUploadRequest image)
{
this.WhenActivated(d =>
{
Dispatcher.UIThread.Invoke(() =>
{
- imageStream.Seek(0, SeekOrigin.Begin);
- Bitmap = new Bitmap(imageStream);
- FileSize = imageStream.Length;
+ image.File.Seek(0, SeekOrigin.Begin);
+ Bitmap = new Bitmap(image.File);
+ FileSize = image.File.Length;
ImageDimensions = Bitmap.Size.Width + "x" + Bitmap.Size.Height;
-
- if (imageStream is FileStream fileStream)
- FileName = Path.GetFileName(fileStream.Name);
- else
- FileName = "Unnamed image";
+ Name = image.Name;
+ Description = image.Description;
Bitmap.DisposeWith(d);
}, DispatcherPriority.Background);
});
+
+ this.ValidationRule(vm => vm.Name, input => !string.IsNullOrWhiteSpace(input), "Name is required");
+ this.ValidationRule(vm => vm.Name, input => input?.Length <= 50, "Name can be a maximum of 50 characters");
+ this.ValidationRule(vm => vm.Description, input => input?.Length <= 150, "Description can be a maximum of 150 characters");
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Layout/LayoutDetailsView.axaml b/src/Artemis.UI/Screens/Workshop/Layout/LayoutDetailsView.axaml
index 5e953bd10..3b38036a6 100644
--- a/src/Artemis.UI/Screens/Workshop/Layout/LayoutDetailsView.axaml
+++ b/src/Artemis.UI/Screens/Workshop/Layout/LayoutDetailsView.axaml
@@ -2,132 +2,31 @@
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:Layout="clr-namespace:Artemis.UI.Screens.Workshop.Layout"
- xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
- xmlns:il="clr-namespace:AsyncImageLoader;assembly=AsyncImageLoader.Avalonia"
- xmlns:converters="clr-namespace:Artemis.UI.Converters"
+ xmlns:layout="clr-namespace:Artemis.UI.Screens.Workshop.Layout"
xmlns:mdxaml="https://github.com/whistyun/Markdown.Avalonia.Tight"
- xmlns:mdsvg="https://github.com/whistyun/Markdown.Avalonia.Svg"
- xmlns:ui="clr-namespace:Artemis.UI"
- xmlns:converters1="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="800"
x:Class="Artemis.UI.Screens.Workshop.Layout.LayoutDetailsView"
- x:DataType="Layout:LayoutDetailsViewModel">
-
-
-
-
-
-
+ x:DataType="layout:LayoutDetailsViewModel">
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- downloads
-
-
-
-
- Created
-
-
-
-
- Updated
-
-
-
+
-
-
- Latest release
-
-
-
+
-
-
+
+
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Layout/LayoutDetailsViewModel.cs b/src/Artemis.UI/Screens/Workshop/Layout/LayoutDetailsViewModel.cs
index f1b3c9da3..de7fb32ea 100644
--- a/src/Artemis.UI/Screens/Workshop/Layout/LayoutDetailsViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/Layout/LayoutDetailsViewModel.cs
@@ -1,17 +1,11 @@
using System;
-using System.Reactive;
-using System.Reactive.Linq;
using System.Threading;
using System.Threading.Tasks;
-using Artemis.Core;
+using Artemis.UI.Screens.Workshop.Entries.Details;
using Artemis.UI.Screens.Workshop.Parameters;
using Artemis.UI.Shared.Routing;
-using Artemis.UI.Shared.Services;
-using Artemis.UI.Shared.Services.Builders;
-using Artemis.UI.Shared.Utilities;
using Artemis.WebClient.Workshop;
using PropertyChanged.SourceGenerator;
-using ReactiveUI;
using StrawberryShake;
namespace Artemis.UI.Screens.Workshop.Layout;
@@ -19,25 +13,25 @@ namespace Artemis.UI.Screens.Workshop.Layout;
public partial class LayoutDetailsViewModel : RoutableScreen
{
private readonly IWorkshopClient _client;
- private readonly INotificationService _notificationService;
- private readonly IWindowService _windowService;
- private readonly ObservableAsPropertyHelper _updatedAt;
- [Notify(Setter.Private)] private IGetEntryById_Entry? _entry;
+ private readonly Func _getEntryInfoViewModel;
+ private readonly Func _getEntryReleasesViewModel;
+ private readonly Func _getEntryImagesViewModel;
+ [Notify] private IGetEntryById_Entry? _entry;
+ [Notify] private EntryInfoViewModel? _entryInfoViewModel;
+ [Notify] private EntryReleasesViewModel? _entryReleasesViewModel;
+ [Notify] private EntryImagesViewModel? _entryImagesViewModel;
- public LayoutDetailsViewModel(IWorkshopClient client, INotificationService notificationService, IWindowService windowService)
+ public LayoutDetailsViewModel(IWorkshopClient client,
+ Func getEntryInfoViewModel,
+ Func getEntryReleasesViewModel,
+ Func getEntryImagesViewModel)
{
_client = client;
- _notificationService = notificationService;
- _windowService = windowService;
- _updatedAt = this.WhenAnyValue(vm => vm.Entry).Select(e => e?.LatestRelease?.CreatedAt ?? e?.CreatedAt).ToProperty(this, vm => vm.UpdatedAt);
-
- DownloadLatestRelease = ReactiveCommand.CreateFromTask(ExecuteDownloadLatestRelease);
+ _getEntryInfoViewModel = getEntryInfoViewModel;
+ _getEntryReleasesViewModel = getEntryReleasesViewModel;
+ _getEntryImagesViewModel = getEntryImagesViewModel;
}
- public ReactiveCommand DownloadLatestRelease { get; }
-
- public DateTimeOffset? UpdatedAt => _updatedAt.Value;
-
public override async Task OnNavigating(WorkshopDetailParameters parameters, NavigationArguments args, CancellationToken cancellationToken)
{
await GetEntry(parameters.EntryId, cancellationToken);
@@ -50,10 +44,16 @@ public partial class LayoutDetailsViewModel : RoutableScreen
diff --git a/src/Artemis.UI/Screens/Workshop/Layout/LayoutInfoViewModel.cs b/src/Artemis.UI/Screens/Workshop/Layout/LayoutInfoViewModel.cs
index b34228082..166652cad 100644
--- a/src/Artemis.UI/Screens/Workshop/Layout/LayoutInfoViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/Layout/LayoutInfoViewModel.cs
@@ -51,6 +51,7 @@ public partial class LayoutInfoViewModel : ValidatableViewModelBase
this.ValidationRule(vm => vm.Vendor, input => !string.IsNullOrWhiteSpace(input), "Device vendor is required");
this.ValidationRule(vm => vm.DeviceProviderIdInput, input => Guid.TryParse(input, out _), "Must be a valid GUID formatted as: 00000000-0000-0000-0000-000000000000");
this.ValidationRule(vm => vm.DeviceProviderIdInput, input => !string.IsNullOrWhiteSpace(input), "Device provider ID is required");
+ this.ValidationRule(vm => vm.DeviceProviderIdInput, input => input != "00000000-0000-0000-0000-000000000000", "Device provider ID is required");
}
public string? DeviceProviders => _deviceProviders.Value;
diff --git a/src/Artemis.UI/Screens/Workshop/Library/SubmissionDetailViewModel.cs b/src/Artemis.UI/Screens/Workshop/Library/SubmissionDetailViewModel.cs
index 379d3274f..e2acea559 100644
--- a/src/Artemis.UI/Screens/Workshop/Library/SubmissionDetailViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/Library/SubmissionDetailViewModel.cs
@@ -21,6 +21,7 @@ using Avalonia.Media.Imaging;
using PropertyChanged.SourceGenerator;
using ReactiveUI;
using StrawberryShake;
+using EntrySpecificationsViewModel = Artemis.UI.Screens.Workshop.Entries.Details.EntrySpecificationsViewModel;
namespace Artemis.UI.Screens.Workshop.Library;
diff --git a/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsView.axaml b/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsView.axaml
index 2ca2544a5..af516a4f0 100644
--- a/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsView.axaml
+++ b/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsView.axaml
@@ -3,141 +3,30 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:profile="clr-namespace:Artemis.UI.Screens.Workshop.Profile"
- xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
- xmlns:il="clr-namespace:AsyncImageLoader;assembly=AsyncImageLoader.Avalonia"
- xmlns:converters="clr-namespace:Artemis.UI.Converters"
xmlns:mdxaml="https://github.com/whistyun/Markdown.Avalonia.Tight"
- xmlns:mdsvg="https://github.com/whistyun/Markdown.Avalonia.Svg"
- xmlns:ui="clr-namespace:Artemis.UI"
- xmlns:converters1="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="800"
x:Class="Artemis.UI.Screens.Workshop.Profile.ProfileDetailsView"
x:DataType="profile:ProfileDetailsViewModel">
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- downloads
-
-
-
-
- Created
-
-
-
-
- Updated
-
-
-
+
-
-
- Latest release
-
-
-
+
-
-
+
+
+
+
\ 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 c413c9eee..6c65b78c4 100644
--- a/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsViewModel.cs
@@ -1,18 +1,11 @@
using System;
-using System.Reactive;
-using System.Reactive.Linq;
using System.Threading;
using System.Threading.Tasks;
-using Artemis.Core;
+using Artemis.UI.Screens.Workshop.Entries.Details;
using Artemis.UI.Screens.Workshop.Parameters;
using Artemis.UI.Shared.Routing;
-using Artemis.UI.Shared.Services;
-using Artemis.UI.Shared.Services.Builders;
-using Artemis.UI.Shared.Utilities;
using Artemis.WebClient.Workshop;
-using Artemis.WebClient.Workshop.Handlers.InstallationHandlers;
using PropertyChanged.SourceGenerator;
-using ReactiveUI;
using StrawberryShake;
namespace Artemis.UI.Screens.Workshop.Profile;
@@ -20,30 +13,25 @@ namespace Artemis.UI.Screens.Workshop.Profile;
public partial class ProfileDetailsViewModel : RoutableScreen
{
private readonly IWorkshopClient _client;
- private readonly ProfileEntryInstallationHandler _installationHandler;
- private readonly INotificationService _notificationService;
- private readonly ObservableAsPropertyHelper _updatedAt;
- private readonly IWindowService _windowService;
- [Notify(Setter.Private)] private IGetEntryById_Entry? _entry;
+ private readonly Func _getEntryInfoViewModel;
+ private readonly Func _getEntryReleasesViewModel;
+ private readonly Func _getEntryImagesViewModel;
+ [Notify] private IGetEntryById_Entry? _entry;
+ [Notify] private EntryInfoViewModel? _entryInfoViewModel;
+ [Notify] private EntryReleasesViewModel? _entryReleasesViewModel;
+ [Notify] private EntryImagesViewModel? _entryImagesViewModel;
- public ProfileDetailsViewModel(IWorkshopClient client, ProfileEntryInstallationHandler installationHandler, INotificationService notificationService, IWindowService windowService)
+ public ProfileDetailsViewModel(IWorkshopClient client,
+ Func getEntryInfoViewModel,
+ Func getEntryReleasesViewModel,
+ Func getEntryImagesViewModel)
{
_client = client;
- _installationHandler = installationHandler;
- _notificationService = notificationService;
- _windowService = windowService;
- _updatedAt = this.WhenAnyValue(vm => vm.Entry).Select(e => e?.LatestRelease?.CreatedAt ?? e?.CreatedAt).ToProperty(this, vm => vm.UpdatedAt);
-
- DownloadLatestRelease = ReactiveCommand.CreateFromTask(ExecuteDownloadLatestRelease);
- CopyShareLink = ReactiveCommand.CreateFromTask(ExecuteCopyShareLink);
+ _getEntryInfoViewModel = getEntryInfoViewModel;
+ _getEntryReleasesViewModel = getEntryReleasesViewModel;
+ _getEntryImagesViewModel = getEntryImagesViewModel;
}
- public ReactiveCommand CopyShareLink { get; set; }
-
- public ReactiveCommand DownloadLatestRelease { get; }
-
- public DateTimeOffset? UpdatedAt => _updatedAt.Value;
-
public override async Task OnNavigating(WorkshopDetailParameters parameters, NavigationArguments args, CancellationToken cancellationToken)
{
await GetEntry(parameters.EntryId, cancellationToken);
@@ -56,30 +44,16 @@ public partial class ProfileDetailsViewModel : RoutableScreen(), cancellationToken);
- if (result.IsSuccess)
- _notificationService.CreateNotification().WithTitle("Profile installed").WithSeverity(NotificationSeverity.Success).Show();
- else
- _notificationService.CreateNotification().WithTitle("Failed to install profile").WithMessage(result.Message).WithSeverity(NotificationSeverity.Error).Show();
- }
-
- private async Task ExecuteCopyShareLink(CancellationToken arg)
- {
if (Entry == null)
- return;
-
- await Shared.UI.Clipboard.SetTextAsync($"{WorkshopConstants.WORKSHOP_URL}/entries/{Entry.Id}/{StringUtilities.UrlFriendly(Entry.Name)}");
- _notificationService.CreateNotification().WithTitle("Copied share link to clipboard.").Show();
+ {
+ EntryInfoViewModel = null;
+ EntryReleasesViewModel = null;
+ }
+ else
+ {
+ EntryInfoViewModel = _getEntryInfoViewModel(Entry);
+ EntryReleasesViewModel = _getEntryReleasesViewModel(Entry);
+ EntryImagesViewModel = _getEntryImagesViewModel(Entry);
+ }
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Models/SubmissionWizardState.cs b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Models/SubmissionWizardState.cs
index 9b0c79503..89d2f4fa9 100644
--- a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Models/SubmissionWizardState.cs
+++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Models/SubmissionWizardState.cs
@@ -33,7 +33,7 @@ public class SubmissionWizardState : IDisposable
public List Categories { get; set; } = new();
public List Tags { get; set; } = new();
- public List Images { get; set; } = new();
+ public List Images { get; set; } = new();
public IEntrySource? EntrySource { get; set; }
@@ -67,7 +67,7 @@ public class SubmissionWizardState : IDisposable
public void Dispose()
{
Icon?.Dispose();
- foreach (Stream stream in Images)
- stream.Dispose();
+ foreach (ImageUploadRequest image in Images)
+ image.File.Dispose();
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/ImagesStepViewModel.cs b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/ImagesStepViewModel.cs
index 411bf7fb3..2a430347f 100644
--- a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/ImagesStepViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/ImagesStepViewModel.cs
@@ -7,6 +7,7 @@ using System.Threading;
using System.Threading.Tasks;
using Artemis.UI.Screens.Workshop.Image;
using Artemis.UI.Shared.Services;
+using Artemis.WebClient.Workshop.Handlers.UploadHandlers;
using DynamicData;
using ReactiveUI;
@@ -16,37 +17,39 @@ public class ImagesStepViewModel : SubmissionViewModel
{
private const long MAX_FILE_SIZE = 10 * 1024 * 1024; // 10 MB
private readonly IWindowService _windowService;
- private readonly SourceList _imageStreams;
+ private readonly Func _getImageSubmissionViewModel;
+ private readonly SourceList _stateImages;
- public ImagesStepViewModel(IWindowService windowService, Func imageSubmissionViewModel)
+ public ImagesStepViewModel(IWindowService windowService, Func getImageSubmissionViewModel)
{
_windowService = windowService;
+ _getImageSubmissionViewModel = getImageSubmissionViewModel;
Continue = ReactiveCommand.Create(() => State.ChangeScreen());
GoBack = ReactiveCommand.Create(() => State.ChangeScreen());
Secondary = ReactiveCommand.CreateFromTask(ExecuteAddImage);
SecondaryText = "Add image";
- _imageStreams = new SourceList();
- _imageStreams.Connect()
- .Transform(p => CreateImageSubmissionViewModel(imageSubmissionViewModel, p))
+ _stateImages = new SourceList();
+ _stateImages.Connect()
+ .Transform(p => CreateImageSubmissionViewModel(p))
.Bind(out ReadOnlyObservableCollection images)
.Subscribe();
Images = images;
this.WhenActivated((CompositeDisposable d) =>
{
- _imageStreams.Clear();
- _imageStreams.AddRange(State.Images);
+ _stateImages.Clear();
+ _stateImages.AddRange(State.Images);
});
}
public ReadOnlyObservableCollection Images { get; }
- private ImageSubmissionViewModel CreateImageSubmissionViewModel(Func imageSubmissionViewModel, Stream stream)
+ private ImageSubmissionViewModel CreateImageSubmissionViewModel(ImageUploadRequest image)
{
- ImageSubmissionViewModel viewModel = imageSubmissionViewModel(stream);
- viewModel.Remove = ReactiveCommand.Create(() => _imageStreams.Remove(stream));
+ ImageSubmissionViewModel viewModel = _getImageSubmissionViewModel(image);
+ viewModel.Remove = ReactiveCommand.Create(() => _stateImages.Remove(image));
return viewModel;
}
@@ -58,7 +61,7 @@ public class ImagesStepViewModel : SubmissionViewModel
foreach (string path in result)
{
- if (_imageStreams.Items.Any(i => i is FileStream fs && fs.Name == path))
+ if (_stateImages.Items.Any(i => i.File is FileStream fs && fs.Name == path))
continue;
FileStream stream = new(path, FileMode.Open, FileAccess.Read);
@@ -69,8 +72,9 @@ public class ImagesStepViewModel : SubmissionViewModel
continue;
}
- _imageStreams.Add(stream);
- State.Images.Add(stream);
+ ImageUploadRequest request = new(stream, Path.GetFileName(path), string.Empty);
+ _stateImages.Add(request);
+ State.Images.Add(request);
}
}
}
\ No newline at end of file
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 69e4709c1..cdbdec029 100644
--- a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Layout/LayoutSelectionStepViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Layout/LayoutSelectionStepViewModel.cs
@@ -166,14 +166,14 @@ public partial class LayoutSelectionStepViewModel : SubmissionViewModel
}
State.Icon?.Dispose();
- foreach (Stream stateImage in State.Images)
- stateImage.Dispose();
+ foreach (ImageUploadRequest stateImage in State.Images)
+ stateImage.File.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);
- State.Images.Add(deviceWithLeds);
+ State.Images.Add(new ImageUploadRequest(deviceWithoutLeds, "Layout preview (no LEDs)", "A preview of the device without its LEDs"));
+ State.Images.Add(new ImageUploadRequest(deviceWithLeds, "Layout preview (with LEDs)", "A preview of the device with its LEDs"));
}
private Stream ResizeImage(Stream image, int size)
diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/SpecificationsStepViewModel.cs b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/SpecificationsStepViewModel.cs
index 0f577e1fd..bf9ea0f6e 100644
--- a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/SpecificationsStepViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/SpecificationsStepViewModel.cs
@@ -11,6 +11,7 @@ using Artemis.WebClient.Workshop;
using DynamicData;
using PropertyChanged.SourceGenerator;
using ReactiveUI;
+using EntrySpecificationsViewModel = Artemis.UI.Screens.Workshop.Entries.Details.EntrySpecificationsViewModel;
namespace Artemis.UI.Screens.Workshop.SubmissionWizard.Steps;
diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/UploadStepViewModel.cs b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/UploadStepViewModel.cs
index 89814a7fa..bf6c4f395 100644
--- a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/UploadStepViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/UploadStepViewModel.cs
@@ -133,12 +133,12 @@ public partial class UploadStepViewModel : SubmissionViewModel
return null;
}
- foreach (Stream image in State.Images.ToList())
+ foreach (ImageUploadRequest image in State.Images.ToList())
{
// Upload image
try
{
- ImageUploadResult imageUploadResult = await _workshopService.UploadEntryImage(entryId.Value, _progress, image, cancellationToken);
+ ImageUploadResult imageUploadResult = await _workshopService.UploadEntryImage(entryId.Value, image, _progress, cancellationToken);
if (!imageUploadResult.IsSuccess)
throw new ArtemisWorkshopException(imageUploadResult.Message);
State.Images.Remove(image);
diff --git a/src/Artemis.WebClient.Workshop/Artemis.WebClient.Workshop.csproj b/src/Artemis.WebClient.Workshop/Artemis.WebClient.Workshop.csproj
index 0f6acb5f9..a4f710127 100644
--- a/src/Artemis.WebClient.Workshop/Artemis.WebClient.Workshop.csproj
+++ b/src/Artemis.WebClient.Workshop/Artemis.WebClient.Workshop.csproj
@@ -15,7 +15,7 @@
-
+
diff --git a/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/EntryInstallationHandlerFactory.cs b/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/EntryInstallationHandlerFactory.cs
index 9153d7c5a..c1e35a274 100644
--- a/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/EntryInstallationHandlerFactory.cs
+++ b/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/EntryInstallationHandlerFactory.cs
@@ -16,6 +16,7 @@ public class EntryInstallationHandlerFactory
return entryType switch
{
EntryType.Profile => _container.Resolve(),
+ EntryType.Layout => _container.Resolve(),
_ => throw new NotSupportedException($"EntryType '{entryType}' is not supported.")
};
}
diff --git a/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/LayoutEntryInstallationHandler.cs b/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/LayoutEntryInstallationHandler.cs
new file mode 100644
index 000000000..575048c4f
--- /dev/null
+++ b/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/LayoutEntryInstallationHandler.cs
@@ -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 InstallAsync(IGetEntryById_Entry entry, long releaseId, Progress 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 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");
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.WebClient.Workshop/Handlers/UploadHandlers/ImageUploadRequest.cs b/src/Artemis.WebClient.Workshop/Handlers/UploadHandlers/ImageUploadRequest.cs
new file mode 100644
index 000000000..0bb2e4774
--- /dev/null
+++ b/src/Artemis.WebClient.Workshop/Handlers/UploadHandlers/ImageUploadRequest.cs
@@ -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; }
+}
\ No newline at end of file
diff --git a/src/Artemis.WebClient.Workshop/Handlers/UploadHandlers/Implementations/LayoutEntryUploadHandler.cs b/src/Artemis.WebClient.Workshop/Handlers/UploadHandlers/Implementations/LayoutEntryUploadHandler.cs
index d61a63472..2e16a085e 100644
--- a/src/Artemis.WebClient.Workshop/Handlers/UploadHandlers/Implementations/LayoutEntryUploadHandler.cs
+++ b/src/Artemis.WebClient.Workshop/Handlers/UploadHandlers/Implementations/LayoutEntryUploadHandler.cs
@@ -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;
+ }
+
///
public async Task CreateReleaseAsync(long entryId, IEntrySource entrySource, Progress 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(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)
diff --git a/src/Artemis.WebClient.Workshop/Queries/Fragments.graphql b/src/Artemis.WebClient.Workshop/Queries/Fragments.graphql
index 38e33eb77..9dc7949ea 100644
--- a/src/Artemis.WebClient.Workshop/Queries/Fragments.graphql
+++ b/src/Artemis.WebClient.Workshop/Queries/Fragments.graphql
@@ -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
+ }
}
\ No newline at end of file
diff --git a/src/Artemis.WebClient.Workshop/Queries/GetEntryById.graphql b/src/Artemis.WebClient.Workshop/Queries/GetEntryById.graphql
index 6467133ae..b71c19890 100644
--- a/src/Artemis.WebClient.Workshop/Queries/GetEntryById.graphql
+++ b/src/Artemis.WebClient.Workshop/Queries/GetEntryById.graphql
@@ -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
}
}
\ 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 57553b4ac..67dd4d310 100644
--- a/src/Artemis.WebClient.Workshop/Services/Interfaces/IWorkshopService.cs
+++ b/src/Artemis.WebClient.Workshop/Services/Interfaces/IWorkshopService.cs
@@ -7,7 +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 UploadEntryImage(long entryId, ImageUploadRequest request, Progress progress, 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 c2b343981..0a5719b71 100644
--- a/src/Artemis.WebClient.Workshop/Services/WorkshopService.cs
+++ b/src/Artemis.WebClient.Workshop/Services/WorkshopService.cs
@@ -57,19 +57,19 @@ public class WorkshopService : IWorkshopService
}
///
- public async Task UploadEntryImage(long entryId, Progress progress, Stream image, CancellationToken cancellationToken)
+ public async Task UploadEntryImage(long entryId, ImageUploadRequest request, Progress 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)
diff --git a/src/Artemis.WebClient.Workshop/schema.graphql b/src/Artemis.WebClient.Workshop/schema.graphql
index c79569c64..8639411bf 100644
--- a/src/Artemis.WebClient.Workshop/schema.graphql
+++ b/src/Artemis.WebClient.Workshop/schema.graphql
@@ -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 {