diff --git a/src/Artemis.UI.Shared/Styles/Controls/DataModelPicker.axaml b/src/Artemis.UI.Shared/Styles/Controls/DataModelPicker.axaml
index d2757ca40..0175ae880 100644
--- a/src/Artemis.UI.Shared/Styles/Controls/DataModelPicker.axaml
+++ b/src/Artemis.UI.Shared/Styles/Controls/DataModelPicker.axaml
@@ -67,6 +67,7 @@
@@ -81,7 +82,7 @@
IsVisible="{CompiledBinding IsEventPicker, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type dataModelPicker:DataModelPicker}}}"/>
-
+
@@ -91,6 +92,7 @@
diff --git a/src/Artemis.UI/Screens/Debugger/Tabs/DataModel/DataModelDebugView.axaml b/src/Artemis.UI/Screens/Debugger/Tabs/DataModel/DataModelDebugView.axaml
index 1ae47d2af..cd12f8f18 100644
--- a/src/Artemis.UI/Screens/Debugger/Tabs/DataModel/DataModelDebugView.axaml
+++ b/src/Artemis.UI/Screens/Debugger/Tabs/DataModel/DataModelDebugView.axaml
@@ -52,8 +52,8 @@
diff --git a/src/Artemis.UI/Screens/Debugger/Tabs/Logs/LogsDebugView.axaml b/src/Artemis.UI/Screens/Debugger/Tabs/Logs/LogsDebugView.axaml
index dd438e85b..06ef67204 100644
--- a/src/Artemis.UI/Screens/Debugger/Tabs/Logs/LogsDebugView.axaml
+++ b/src/Artemis.UI/Screens/Debugger/Tabs/Logs/LogsDebugView.axaml
@@ -10,8 +10,8 @@
+ SelectionBrush="{StaticResource TextControlSelectionHighlightColor}"/>
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Debugger/Tabs/Routing/RoutingDebugView.axaml b/src/Artemis.UI/Screens/Debugger/Tabs/Routing/RoutingDebugView.axaml
index 8ea97a606..68230f577 100644
--- a/src/Artemis.UI/Screens/Debugger/Tabs/Routing/RoutingDebugView.axaml
+++ b/src/Artemis.UI/Screens/Debugger/Tabs/Routing/RoutingDebugView.axaml
@@ -20,6 +20,7 @@
diff --git a/src/Artemis.UI/Screens/VisualScripting/CableView.axaml b/src/Artemis.UI/Screens/VisualScripting/CableView.axaml
index 7f9f935a5..31fedf0fa 100644
--- a/src/Artemis.UI/Screens/VisualScripting/CableView.axaml
+++ b/src/Artemis.UI/Screens/VisualScripting/CableView.axaml
@@ -41,7 +41,8 @@
Text="{CompiledBinding Converter={StaticResource SKColorToStringConverter}, Mode=OneWay}"
VerticalAlignment="Center"
HorizontalAlignment="Stretch"
- FontFamily="{StaticResource RobotoMono}" />
+ FontFamily="{StaticResource RobotoMono}"
+ FontSize="13"/>
-
+
-
+
-
+
-
+
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/EntrySpecificationsView.axaml b/src/Artemis.UI/Screens/Workshop/Entries/EntrySpecificationsView.axaml
index 8dbb6539e..94bf49bb5 100644
--- a/src/Artemis.UI/Screens/Workshop/Entries/EntrySpecificationsView.axaml
+++ b/src/Artemis.UI/Screens/Workshop/Entries/EntrySpecificationsView.axaml
@@ -12,7 +12,7 @@
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="800"
x:Class="Artemis.UI.Screens.Workshop.Entries.EntrySpecificationsView"
x:DataType="entries:EntrySpecificationsViewModel">
-
+
@@ -25,8 +25,8 @@
-
-
+
+
+
+
+
+
+
+ Synchronized scrolling
+
+
+
-
-
+
@@ -123,17 +134,12 @@
-
-
-
- Synchronized scrolling
-
-
-
+
+
+ A description is required
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Entries/EntrySpecificationsViewModel.cs b/src/Artemis.UI/Screens/Workshop/Entries/EntrySpecificationsViewModel.cs
index af20fa4b4..c06cde881 100644
--- a/src/Artemis.UI/Screens/Workshop/Entries/EntrySpecificationsViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/Entries/EntrySpecificationsViewModel.cs
@@ -11,6 +11,7 @@ using Artemis.UI.Screens.Workshop.Categories;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using Artemis.WebClient.Workshop;
+using Avalonia.Media.Imaging;
using AvaloniaEdit.Document;
using DynamicData;
using DynamicData.Aggregation;
@@ -19,32 +20,55 @@ using ReactiveUI;
using ReactiveUI.Validation.Extensions;
using ReactiveUI.Validation.Helpers;
using StrawberryShake;
-using Bitmap = Avalonia.Media.Imaging.Bitmap;
namespace Artemis.UI.Screens.Workshop.Entries;
public class EntrySpecificationsViewModel : ValidatableViewModelBase
{
+ private readonly ObservableAsPropertyHelper _categoriesValid;
+ private readonly ObservableAsPropertyHelper _iconValid;
+ private readonly ObservableAsPropertyHelper _descriptionValid;
+ private readonly IWorkshopClient _workshopClient;
private readonly IWindowService _windowService;
- private ObservableAsPropertyHelper? _categoriesValid;
- private ObservableAsPropertyHelper? _iconValid;
+
private string _description = string.Empty;
- private string _name = string.Empty;
- private string _summary = string.Empty;
private Bitmap? _iconBitmap;
private TextDocument? _markdownDocument;
+ private string _name = string.Empty;
+ private string _summary = string.Empty;
+ private bool _iconChanged;
public EntrySpecificationsViewModel(IWorkshopClient workshopClient, IWindowService windowService)
{
+ _workshopClient = workshopClient;
_windowService = windowService;
SelectIcon = ReactiveCommand.CreateFromTask(ExecuteSelectIcon);
+
+ Categories.ToObservableChangeSet()
+ .AutoRefresh(c => c.IsSelected)
+ .Filter(c => c.IsSelected)
+ .Transform(c => c.Id)
+ .Bind(out ReadOnlyObservableCollection selectedCategories)
+ .Subscribe();
+ SelectedCategories = selectedCategories;
- this.WhenActivated(d =>
+ 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 categoriesRule = this.ValidationRule(vm => vm.Categories, Categories.ToObservableChangeSet().AutoRefresh(c => c.IsSelected).Filter(c => c.IsSelected).IsNotEmpty(),
+ "At least one category must be selected"
+ );
+ _iconValid = iconRule.ValidationChanged.Select(c => c.IsValid).ToProperty(this, vm => vm.IconValid);
+ _categoriesValid = categoriesRule.ValidationChanged.Select(c => c.IsValid).ToProperty(this, vm => vm.CategoriesValid);
+ _descriptionValid = descriptionRule.ValidationChanged.Select(c => c.IsValid).ToProperty(this, vm => vm.DescriptionValid);
+
+ this.WhenActivatedAsync(async d =>
{
// Load categories
- Observable.FromAsync(workshopClient.GetCategories.ExecuteAsync).Subscribe(PopulateCategories).DisposeWith(d);
-
- this.ClearValidationRules();
+ await PopulateCategories();
MarkdownDocument = new TextDocument(new StringTextSource(Description));
MarkdownDocument.TextChanged += MarkdownDocumentOnTextChanged;
@@ -57,17 +81,15 @@ public class EntrySpecificationsViewModel : ValidatableViewModelBase
});
}
- private void MarkdownDocumentOnTextChanged(object? sender, EventArgs e)
- {
- Description = MarkdownDocument?.Text ?? string.Empty;
- }
-
public ReactiveCommand SelectIcon { get; }
public ObservableCollection Categories { get; } = new();
public ObservableCollection Tags { get; } = new();
- public bool CategoriesValid => _categoriesValid?.Value ?? true;
- public bool IconValid => _iconValid?.Value ?? true;
+ public ReadOnlyObservableCollection SelectedCategories { get; }
+
+ public bool CategoriesValid => _categoriesValid.Value ;
+ public bool IconValid => _iconValid.Value;
+ public bool DescriptionValid => _descriptionValid.Value;
public string Name
{
@@ -99,23 +121,17 @@ public class EntrySpecificationsViewModel : ValidatableViewModelBase
set => RaiseAndSetIfChanged(ref _markdownDocument, value);
}
- public List PreselectedCategories { get; set; } = new List();
-
- public void SetupDataValidation()
+ public bool IconChanged
{
- // Hopefully this can be avoided in the future
- // https://github.com/reactiveui/ReactiveUI.Validation/discussions/558
- this.ValidationRule(vm => vm.Name, s => !string.IsNullOrWhiteSpace(s), "Name is required");
- this.ValidationRule(vm => vm.Summary, s => !string.IsNullOrWhiteSpace(s), "Summary is required");
- this.ValidationRule(vm => vm.Description, s => !string.IsNullOrWhiteSpace(s), "Description is required");
+ get => _iconChanged;
+ private set => RaiseAndSetIfChanged(ref _iconChanged, value);
+ }
- // 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 categoriesRule = this.ValidationRule(vm => vm.Categories, Categories.ToObservableChangeSet().AutoRefresh(c => c.IsSelected).Filter(c => c.IsSelected).IsNotEmpty(),
- "At least one category must be selected"
- );
- _iconValid = iconRule.ValidationChanged.Select(c => c.IsValid).ToProperty(this, vm => vm.IconValid);
- _categoriesValid = categoriesRule.ValidationChanged.Select(c => c.IsValid).ToProperty(this, vm => vm.CategoriesValid);
+ public List PreselectedCategories { get; set; } = new();
+
+ private void MarkdownDocumentOnTextChanged(object? sender, EventArgs e)
+ {
+ Description = MarkdownDocument?.Text ?? string.Empty;
}
private async Task ExecuteSelectIcon()
@@ -129,6 +145,7 @@ public class EntrySpecificationsViewModel : ValidatableViewModelBase
IconBitmap?.Dispose();
IconBitmap = BitmapExtensions.LoadAndResize(result[0], 128);
+ IconChanged = true;
}
private void ClearIcon()
@@ -137,10 +154,11 @@ public class EntrySpecificationsViewModel : ValidatableViewModelBase
IconBitmap = null;
}
- private void PopulateCategories(IOperationResult result)
+ private async Task PopulateCategories()
{
+ IOperationResult categories = await _workshopClient.GetCategories.ExecuteAsync();
Categories.Clear();
- if (result.Data != null)
- Categories.AddRange(result.Data.Categories.Select(c => new CategoryViewModel(c) {IsSelected = PreselectedCategories.Contains(c.Id)}));
+ if (categories.Data != null)
+ Categories.AddRange(categories.Data.Categories.Select(c => new CategoryViewModel(c) {IsSelected = PreselectedCategories.Contains(c.Id)}));
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Library/SubmissionDetailView.axaml b/src/Artemis.UI/Screens/Workshop/Library/SubmissionDetailView.axaml
index c42098365..99365cb52 100644
--- a/src/Artemis.UI/Screens/Workshop/Library/SubmissionDetailView.axaml
+++ b/src/Artemis.UI/Screens/Workshop/Library/SubmissionDetailView.axaml
@@ -12,8 +12,8 @@
-
-
+
+
Management
@@ -48,7 +48,11 @@
View workshop page
-
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Library/SubmissionDetailViewModel.cs b/src/Artemis.UI/Screens/Workshop/Library/SubmissionDetailViewModel.cs
index 11d35afb1..2b05edc76 100644
--- a/src/Artemis.UI/Screens/Workshop/Library/SubmissionDetailViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/Library/SubmissionDetailViewModel.cs
@@ -1,7 +1,9 @@
using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.ComponentModel;
using System.IO;
using System.Linq;
-using System.Net.Http;
using System.Reactive;
using System.Threading;
using System.Threading.Tasks;
@@ -10,8 +12,11 @@ using Artemis.UI.Screens.Workshop.Parameters;
using Artemis.UI.Screens.Workshop.SubmissionWizard;
using Artemis.UI.Shared.Routing;
using Artemis.UI.Shared.Services;
+using Artemis.UI.Shared.Utilities;
using Artemis.WebClient.Workshop;
+using Artemis.WebClient.Workshop.Exceptions;
using Artemis.WebClient.Workshop.Services;
+using Artemis.WebClient.Workshop.UploadHandlers;
using Avalonia.Media.Imaging;
using ReactiveUI;
using StrawberryShake;
@@ -21,37 +26,33 @@ namespace Artemis.UI.Screens.Workshop.Library;
public class SubmissionDetailViewModel : RoutableScreen
{
private readonly IWorkshopClient _client;
- private readonly IHttpClientFactory _httpClientFactory;
- private readonly Func _getEntrySpecificationsViewModel;
+ private readonly Func _getGetSpecificationsVm;
+ private readonly IRouter _router;
private readonly IWindowService _windowService;
private readonly IWorkshopService _workshopService;
- private readonly IRouter _router;
private IGetSubmittedEntryById_Entry? _entry;
private EntrySpecificationsViewModel? _entrySpecificationsViewModel;
+ private bool _hasChanges;
- public SubmissionDetailViewModel(IWorkshopClient client,
- IHttpClientFactory httpClientFactory,
- IWindowService windowService,
- IWorkshopService workshopService,
- IRouter router,
- Func entrySpecificationsViewModel)
- {
+ public SubmissionDetailViewModel(IWorkshopClient client, IWindowService windowService, IWorkshopService workshopService, IRouter router, Func getSpecificationsVm) {
_client = client;
- _httpClientFactory = httpClientFactory;
_windowService = windowService;
_workshopService = workshopService;
_router = router;
- _getEntrySpecificationsViewModel = entrySpecificationsViewModel;
+ _getGetSpecificationsVm = getSpecificationsVm;
- Save = ReactiveCommand.CreateFromTask(ExecuteSave);
CreateRelease = ReactiveCommand.CreateFromTask(ExecuteCreateRelease);
DeleteSubmission = ReactiveCommand.CreateFromTask(ExecuteDeleteSubmission);
ViewWorkshopPage = ReactiveCommand.CreateFromTask(ExecuteViewWorkshopPage);
+ DiscardChanges = ReactiveCommand.CreateFromTask(ExecuteDiscardChanges, this.WhenAnyValue(vm => vm.HasChanges));
+ SaveChanges = ReactiveCommand.CreateFromTask(ExecuteSaveChanges, this.WhenAnyValue(vm => vm.HasChanges));
}
- public ReactiveCommand Save { get; }
public ReactiveCommand CreateRelease { get; }
public ReactiveCommand DeleteSubmission { get; }
+ public ReactiveCommand ViewWorkshopPage { get; }
+ public ReactiveCommand SaveChanges { get; }
+ public ReactiveCommand DiscardChanges { get; }
public EntrySpecificationsViewModel? EntrySpecificationsViewModel
{
@@ -65,8 +66,12 @@ public class SubmissionDetailViewModel : RoutableScreen RaiseAndSetIfChanged(ref _entry, value);
}
- public ReactiveCommand ViewWorkshopPage { get; }
-
+ public bool HasChanges
+ {
+ get => _hasChanges;
+ private set => RaiseAndSetIfChanged(ref _hasChanges, value);
+ }
+
public override async Task OnNavigating(WorkshopDetailParameters parameters, NavigationArguments args, CancellationToken cancellationToken)
{
IOperationResult result = await _client.GetSubmittedEntryById.ExecuteAsync(parameters.EntryId, cancellationToken);
@@ -77,12 +82,29 @@ public class SubmissionDetailViewModel : RoutableScreen GetEntryIcon(CancellationToken cancellationToken)
@@ -102,26 +127,59 @@ public class SubmissionDetailViewModel : RoutableScreen categories = EntrySpecificationsViewModel.Categories.Where(c => c.IsSelected).Select(c => c.Id).OrderBy(c => c).ToList();
+ List tags = EntrySpecificationsViewModel.Tags.OrderBy(t => t).ToList();
+
+ HasChanges = EntrySpecificationsViewModel.Name != Entry.Name ||
+ EntrySpecificationsViewModel.Description != Entry.Description ||
+ EntrySpecificationsViewModel.Summary != Entry.Summary ||
+ EntrySpecificationsViewModel.IconChanged ||
+ !tags.SequenceEqual(Entry.Tags.Select(t => t.Name).OrderBy(t => t)) ||
+ !categories.SequenceEqual(Entry.Categories.Select(c => c.Id).OrderBy(c => c));
+ }
+
+ private async Task ExecuteDiscardChanges()
+ {
+ await ApplyFromEntry(CancellationToken.None);
+ }
+
+ private async Task ExecuteSaveChanges(CancellationToken cancellationToken)
+ {
+ if (Entry == null || EntrySpecificationsViewModel == null || !EntrySpecificationsViewModel.ValidationContext.GetIsValid())
+ return;
+
+ UpdateEntryInput input = new()
+ {
+ Id = Entry.Id,
+ Name = EntrySpecificationsViewModel.Name,
+ Summary = EntrySpecificationsViewModel.Summary,
+ Description = EntrySpecificationsViewModel.Description,
+ Categories = EntrySpecificationsViewModel.SelectedCategories,
+ Tags = EntrySpecificationsViewModel.Tags
+ };
+
IOperationResult result = await _client.UpdateEntry.ExecuteAsync(input, cancellationToken);
result.EnsureNoErrors();
+
+ if (EntrySpecificationsViewModel.IconChanged && EntrySpecificationsViewModel.IconBitmap != null)
+ {
+ using MemoryStream stream = new();
+ EntrySpecificationsViewModel.IconBitmap.Save(stream);
+ ImageUploadResult imageResult = await _workshopService.SetEntryIcon(Entry.Id, new Progress(), stream, cancellationToken);
+ if (!imageResult.IsSuccess)
+ throw new ArtemisWorkshopException("Failed to upload image. " + imageResult.Message);
+ }
+
+ HasChanges = false;
}
private async Task ExecuteCreateRelease(CancellationToken cancellationToken)
@@ -152,4 +210,19 @@ public class SubmissionDetailViewModel : RoutableScreen RaiseAndSetIfChanged(ref _selectedEntryType, value);
}
- ///
- public override ReactiveCommand Continue { get; }
-
- ///
- public override ReactiveCommand GoBack { get; }
-
private void ExecuteContinue()
{
if (SelectedEntryType == null)
diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/LoginStepViewModel.cs b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/LoginStepViewModel.cs
index 83b7d3ca0..5b6cf680b 100644
--- a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/LoginStepViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/LoginStepViewModel.cs
@@ -1,6 +1,4 @@
-using System;
using System.Linq;
-using System.Reactive;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
@@ -29,12 +27,6 @@ public class LoginStepViewModel : SubmissionViewModel
ContinueText = "Log In";
}
- ///
- public override ReactiveCommand Continue { get; }
-
- ///
- public override ReactiveCommand GoBack { get; } = null!;
-
private async Task ExecuteLogin(CancellationToken ct)
{
ContentDialogResult result = await _windowService.CreateContentDialog().WithViewModel(out WorkshopLoginViewModel _).WithTitle("Workshop login").ShowAsync();
diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Profile/ProfileAdaptionHintsLayerViewModel.cs b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Profile/ProfileAdaptionHintsLayerViewModel.cs
index ade952508..48b741698 100644
--- a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Profile/ProfileAdaptionHintsLayerViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Profile/ProfileAdaptionHintsLayerViewModel.cs
@@ -6,31 +6,30 @@ using Artemis.Core.Services;
using Artemis.UI.Screens.ProfileEditor.ProfileTree.Dialogs;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
-using FluentAvalonia.Core;
using ReactiveUI;
namespace Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.Profile;
public class ProfileAdaptionHintsLayerViewModel : ViewModelBase
{
- private readonly IWindowService _windowService;
- private readonly IProfileService _profileService;
private readonly ObservableAsPropertyHelper _adaptionHintText;
+ private readonly IProfileService _profileService;
+ private readonly IWindowService _windowService;
private int _adaptionHintCount;
- public Layer Layer { get; }
-
public ProfileAdaptionHintsLayerViewModel(Layer layer, IWindowService windowService, IProfileService profileService)
{
_windowService = windowService;
_profileService = profileService;
_adaptionHintText = this.WhenAnyValue(vm => vm.AdaptionHintCount).Select(c => c == 1 ? "1 adaption hint" : $"{c} adaption hints").ToProperty(this, vm => vm.AdaptionHintText);
-
+
Layer = layer;
EditAdaptionHints = ReactiveCommand.CreateFromTask(ExecuteEditAdaptionHints);
AdaptionHintCount = layer.Adapter.AdaptionHints.Count;
}
+ public Layer Layer { get; }
+
public ReactiveCommand EditAdaptionHints { get; }
public int AdaptionHintCount
diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Profile/ProfileAdaptionHintsStepViewModel.cs b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Profile/ProfileAdaptionHintsStepViewModel.cs
index b9ec09c15..fc55b987a 100644
--- a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Profile/ProfileAdaptionHintsStepViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Profile/ProfileAdaptionHintsStepViewModel.cs
@@ -3,23 +3,22 @@ using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive;
using System.Reactive.Disposables;
-using System.Reactive.Linq;
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Screens.ProfileEditor.ProfileTree.Dialogs;
using Artemis.UI.Shared.Services;
using DynamicData;
-using ReactiveUI;
using DynamicData.Aggregation;
+using ReactiveUI;
namespace Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.Profile;
public class ProfileAdaptionHintsStepViewModel : SubmissionViewModel
{
- private readonly IWindowService _windowService;
- private readonly IProfileService _profileService;
private readonly SourceList _layers;
+ private readonly IProfileService _profileService;
+ private readonly IWindowService _windowService;
public ProfileAdaptionHintsStepViewModel(IWindowService windowService, IProfileService profileService, Func getLayerViewModel)
{
@@ -36,18 +35,14 @@ public class ProfileAdaptionHintsStepViewModel : SubmissionViewModel
this.WhenActivated((CompositeDisposable _) =>
{
if (State.EntrySource is ProfileConfiguration profileConfiguration && profileConfiguration.Profile != null)
- {
_layers.Edit(l =>
{
l.Clear();
l.AddRange(profileConfiguration.Profile.GetAllLayers().Select(getLayerViewModel));
});
- }
});
}
- public override ReactiveCommand Continue { get; }
- public override ReactiveCommand GoBack { get; }
public ReactiveCommand EditAdaptionHints { get; }
public ReadOnlyObservableCollection Layers { get; }
@@ -61,7 +56,7 @@ public class ProfileAdaptionHintsStepViewModel : SubmissionViewModel
{
if (Layers.Any(l => l.AdaptionHintCount == 0))
return;
-
+
if (State.EntryId == null)
State.ChangeScreen();
else
diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Profile/ProfileSelectionStepViewModel.cs b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Profile/ProfileSelectionStepViewModel.cs
index 86b49a6d4..adad736d7 100644
--- a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Profile/ProfileSelectionStepViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Profile/ProfileSelectionStepViewModel.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections.ObjectModel;
using System.Linq;
-using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using Artemis.Core;
@@ -52,12 +51,6 @@ public class ProfileSelectionStepViewModel : SubmissionViewModel
set => RaiseAndSetIfChanged(ref _selectedProfile, value);
}
- ///
- public override ReactiveCommand Continue { get; }
-
- ///
- public override ReactiveCommand GoBack { get; }
-
private void Update(ProfileConfiguration? profileConfiguration)
{
ProfilePreview.ProfileConfiguration = null;
diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/SpecificationsStepView.axaml.cs b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/SpecificationsStepView.axaml.cs
index f7d256121..6d05e97a1 100644
--- a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/SpecificationsStepView.axaml.cs
+++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/SpecificationsStepView.axaml.cs
@@ -1,5 +1,3 @@
-using Avalonia;
-using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/SpecificationsStepViewModel.cs b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/SpecificationsStepViewModel.cs
index ebb75cd42..4e8a17c20 100644
--- a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/SpecificationsStepViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/SpecificationsStepViewModel.cs
@@ -2,41 +2,38 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Reactive;
using System.Reactive.Disposables;
using Artemis.UI.Extensions;
using Artemis.UI.Screens.Workshop.Entries;
using Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.Profile;
using Artemis.WebClient.Workshop;
-using Avalonia.Threading;
using DynamicData;
using ReactiveUI;
-using ReactiveUI.Validation.Extensions;
namespace Artemis.UI.Screens.Workshop.SubmissionWizard.Steps;
public class SpecificationsStepViewModel : SubmissionViewModel
{
- public SpecificationsStepViewModel(EntrySpecificationsViewModel entrySpecificationsViewModel)
- {
- EntrySpecificationsViewModel = entrySpecificationsViewModel;
- GoBack = ReactiveCommand.Create(ExecuteGoBack);
- Continue = ReactiveCommand.Create(ExecuteContinue, EntrySpecificationsViewModel.ValidationContext.Valid);
+ private readonly Func _getEntrySpecificationsViewModel;
+ private EntrySpecificationsViewModel? _entrySpecificationsViewModel;
+ public SpecificationsStepViewModel(Func getEntrySpecificationsViewModel)
+ {
+ _getEntrySpecificationsViewModel = getEntrySpecificationsViewModel;
+
+ GoBack = ReactiveCommand.Create(ExecuteGoBack);
this.WhenActivated((CompositeDisposable d) =>
{
DisplayName = $"{State.EntryType} Information";
-
- // Apply the state
ApplyFromState();
-
- EntrySpecificationsViewModel.ClearValidationRules();
});
}
- public EntrySpecificationsViewModel EntrySpecificationsViewModel { get; }
- public override ReactiveCommand Continue { get; }
- public override ReactiveCommand GoBack { get; }
+ public EntrySpecificationsViewModel? EntrySpecificationsViewModel
+ {
+ get => _entrySpecificationsViewModel;
+ set => RaiseAndSetIfChanged(ref _entrySpecificationsViewModel, value);
+ }
private void ExecuteGoBack()
{
@@ -59,46 +56,45 @@ public class SpecificationsStepViewModel : SubmissionViewModel
private void ExecuteContinue()
{
- if (!EntrySpecificationsViewModel.ValidationContext.Validations.Any())
- {
- // The ValidationContext seems to update asynchronously, so stop and schedule a retry
- EntrySpecificationsViewModel.SetupDataValidation();
- Dispatcher.UIThread.Post(ExecuteContinue);
+ if (EntrySpecificationsViewModel == null || !EntrySpecificationsViewModel.ValidationContext.GetIsValid())
return;
- }
ApplyToState();
-
- if (!EntrySpecificationsViewModel.ValidationContext.GetIsValid())
- return;
-
State.ChangeScreen();
}
private void ApplyFromState()
{
+ EntrySpecificationsViewModel viewModel = _getEntrySpecificationsViewModel();
+
// Basic fields
- EntrySpecificationsViewModel.Name = State.Name;
- EntrySpecificationsViewModel.Summary = State.Summary;
- EntrySpecificationsViewModel.Description = State.Description;
+ viewModel.Name = State.Name;
+ viewModel.Summary = State.Summary;
+ viewModel.Description = State.Description;
// Tags
- EntrySpecificationsViewModel.Tags.Clear();
- EntrySpecificationsViewModel.Tags.AddRange(State.Tags);
+ viewModel.Tags.Clear();
+ viewModel.Tags.AddRange(State.Tags);
// Categories
- EntrySpecificationsViewModel.PreselectedCategories = State.Categories;
+ viewModel.PreselectedCategories = State.Categories;
// Icon
if (State.Icon != null)
{
State.Icon.Seek(0, SeekOrigin.Begin);
- EntrySpecificationsViewModel.IconBitmap = BitmapExtensions.LoadAndResize(State.Icon, 128);
+ viewModel.IconBitmap = BitmapExtensions.LoadAndResize(State.Icon, 128);
}
+
+ EntrySpecificationsViewModel = viewModel;
+ Continue = ReactiveCommand.Create(ExecuteContinue, EntrySpecificationsViewModel.ValidationContext.Valid);
}
private void ApplyToState()
{
+ if (EntrySpecificationsViewModel == null)
+ return;
+
// Basic fields
State.Name = EntrySpecificationsViewModel.Name;
State.Summary = EntrySpecificationsViewModel.Summary;
diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/SubmitStepView.axaml.cs b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/SubmitStepView.axaml.cs
index 49f0af7ff..ba64d1d7d 100644
--- a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/SubmitStepView.axaml.cs
+++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/SubmitStepView.axaml.cs
@@ -1,5 +1,3 @@
-using Avalonia;
-using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/SubmitStepViewModel.cs b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/SubmitStepViewModel.cs
index 0752ccc0d..8544c30ef 100644
--- a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/SubmitStepViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/SubmitStepViewModel.cs
@@ -1,17 +1,16 @@
+using System;
using System.Collections.ObjectModel;
+using System.IO;
using System.Linq;
-using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using Artemis.UI.Screens.Workshop.Categories;
using Artemis.WebClient.Workshop;
using Artemis.WebClient.Workshop.Services;
+using Avalonia.Media.Imaging;
using IdentityModel;
using ReactiveUI;
using StrawberryShake;
-using System;
-using System.IO;
-using Avalonia.Media.Imaging;
namespace Artemis.UI.Screens.Workshop.SubmissionWizard.Steps;
@@ -26,9 +25,9 @@ public class SubmitStepViewModel : SubmissionViewModel
CurrentUser = authenticationService.Claims.FirstOrDefault(c => c.Type == JwtClaimTypes.Name)?.Value;
GoBack = ReactiveCommand.Create(() => State.ChangeScreen());
Continue = ReactiveCommand.Create(() => State.ChangeScreen());
-
+
ContinueText = "Submit";
-
+
this.WhenActivated(d =>
{
if (State.Icon != null)
@@ -37,6 +36,7 @@ public class SubmitStepViewModel : SubmissionViewModel
IconBitmap = new Bitmap(State.Icon);
IconBitmap.DisposeWith(d);
}
+
Observable.FromAsync(workshopClient.GetCategories.ExecuteAsync).Subscribe(PopulateCategories).DisposeWith(d);
});
}
@@ -55,19 +55,13 @@ public class SubmitStepViewModel : SubmissionViewModel
set => RaiseAndSetIfChanged(ref _categories, value);
}
- public override ReactiveCommand Continue { get; }
-
- public override ReactiveCommand GoBack { get; }
-
private void PopulateCategories(IOperationResult result)
{
if (result.Data == null)
Categories = null;
else
- {
Categories = new ReadOnlyObservableCollection(
new ObservableCollection(result.Data.Categories.Where(c => State.Categories.Contains(c.Id)).Select(c => new CategoryViewModel(c)))
);
- }
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/UploadStepView.axaml.cs b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/UploadStepView.axaml.cs
index 7dc094d0c..21d6bb439 100644
--- a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/UploadStepView.axaml.cs
+++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/UploadStepView.axaml.cs
@@ -1,5 +1,3 @@
-using Avalonia;
-using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/UploadStepViewModel.cs b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/UploadStepViewModel.cs
index c98e44efa..7d0413714 100644
--- a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/UploadStepViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/UploadStepViewModel.cs
@@ -1,5 +1,4 @@
using System;
-using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Threading;
@@ -55,12 +54,6 @@ public class UploadStepViewModel : SubmissionViewModel
this.WhenActivated(d => Observable.FromAsync(ExecuteUpload).Subscribe().DisposeWith(d));
}
- ///
- public override ReactiveCommand Continue { get; }
-
- ///
- public override ReactiveCommand GoBack { get; } = null!;
-
public int ProgressPercentage => _progressPercentage.Value;
public bool ProgressIndeterminate => _progressIndeterminate.Value;
diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/ValidateEmailStepViewModel.cs b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/ValidateEmailStepViewModel.cs
index 3778c2673..a2a840421 100644
--- a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/ValidateEmailStepViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/ValidateEmailStepViewModel.cs
@@ -27,7 +27,7 @@ public class ValidateEmailStepViewModel : SubmissionViewModel
Continue = ReactiveCommand.Create(ExecuteContinue);
Refresh = ReactiveCommand.CreateFromTask(ExecuteRefresh);
Resend = ReactiveCommand.Create(() => Utilities.OpenUrl(WorkshopConstants.AUTHORITY_URL + "/account/confirm/resend"));
-
+
ShowGoBack = false;
ShowHeader = false;
@@ -43,12 +43,6 @@ public class ValidateEmailStepViewModel : SubmissionViewModel
});
}
- ///
- public override ReactiveCommand Continue { get; }
-
- ///
- public override ReactiveCommand GoBack { get; } = null!;
-
public ReactiveCommand Refresh { get; }
public ReactiveCommand Resend { get; }
diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/WelcomeStepViewModel.cs b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/WelcomeStepViewModel.cs
index 3160fbb1e..87c96fb7d 100644
--- a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/WelcomeStepViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/WelcomeStepViewModel.cs
@@ -1,6 +1,4 @@
-using System;
using System.Linq;
-using System.Reactive;
using System.Threading.Tasks;
using Artemis.WebClient.Workshop.Services;
using IdentityModel;
@@ -21,12 +19,6 @@ public class WelcomeStepViewModel : SubmissionViewModel
ShowGoBack = false;
}
- ///
- public override ReactiveCommand Continue { get; }
-
- ///
- public override ReactiveCommand GoBack { get; } = null!;
-
private async Task ExecuteContinue()
{
bool loggedIn = await _authenticationService.AutoLogin(true);
diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/SubmissionViewModel.cs b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/SubmissionViewModel.cs
index fec465981..282b34590 100644
--- a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/SubmissionViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/SubmissionViewModel.cs
@@ -6,15 +6,26 @@ namespace Artemis.UI.Screens.Workshop.SubmissionWizard;
public abstract class SubmissionViewModel : ValidatableViewModelBase
{
+ private ReactiveCommand? _continue;
+ private ReactiveCommand? _goBack;
private string _continueText = "Continue";
private bool _showFinish;
private bool _showGoBack = true;
private bool _showHeader = true;
public SubmissionWizardState State { get; set; } = null!;
-
- public abstract ReactiveCommand Continue { get; }
- public abstract ReactiveCommand GoBack { get; }
+
+ public ReactiveCommand? Continue
+ {
+ get => _continue;
+ set => RaiseAndSetIfChanged(ref _continue, value);
+ }
+
+ public ReactiveCommand? GoBack
+ {
+ get => _goBack;
+ set => RaiseAndSetIfChanged(ref _goBack, value);
+ }
public bool ShowHeader
{
diff --git a/src/Artemis.VisualScripting/Nodes/Static/Screens/DisplayValueNodeCustomView.axaml b/src/Artemis.VisualScripting/Nodes/Static/Screens/DisplayValueNodeCustomView.axaml
index 014b2bdb5..8831dd1e9 100644
--- a/src/Artemis.VisualScripting/Nodes/Static/Screens/DisplayValueNodeCustomView.axaml
+++ b/src/Artemis.VisualScripting/Nodes/Static/Screens/DisplayValueNodeCustomView.axaml
@@ -46,7 +46,7 @@
-
+
diff --git a/src/Artemis.WebClient.Workshop/Services/IWorkshopService.cs b/src/Artemis.WebClient.Workshop/Services/IWorkshopService.cs
index 5c7dbd35f..d1e0fb287 100644
--- a/src/Artemis.WebClient.Workshop/Services/IWorkshopService.cs
+++ b/src/Artemis.WebClient.Workshop/Services/IWorkshopService.cs
@@ -16,6 +16,22 @@ public class WorkshopService : IWorkshopService
_router = router;
}
+ public async Task GetEntryIcon(Guid entryId, CancellationToken cancellationToken)
+ {
+ HttpClient client = _httpClientFactory.CreateClient(WorkshopConstants.WORKSHOP_CLIENT_NAME);
+ try
+ {
+ HttpResponseMessage response = await client.GetAsync($"entries/{entryId}/icon", cancellationToken);
+ response.EnsureSuccessStatusCode();
+ return await response.Content.ReadAsStreamAsync(cancellationToken);
+ }
+ catch (HttpRequestException)
+ {
+ // ignored
+ return null;
+ }
+ }
+
public async Task SetEntryIcon(Guid entryId, Progress progress, Stream icon, CancellationToken cancellationToken)
{
icon.Seek(0, SeekOrigin.Begin);
@@ -55,7 +71,7 @@ public class WorkshopService : IWorkshopService
return new IWorkshopService.WorkshopStatus(false, e.Message);
}
}
-
+
///
public async Task ValidateWorkshopStatus(CancellationToken cancellationToken)
{
@@ -86,10 +102,11 @@ public class WorkshopService : IWorkshopService
public interface IWorkshopService
{
+ Task GetEntryIcon(Guid entryId, CancellationToken cancellationToken);
Task SetEntryIcon(Guid entryId, Progress progress, Stream icon, CancellationToken cancellationToken);
Task GetWorkshopStatus(CancellationToken cancellationToken);
Task ValidateWorkshopStatus(CancellationToken cancellationToken);
Task NavigateToEntry(Guid entryId, EntryType entryType);
-
+
public record WorkshopStatus(bool IsReachable, string Message);
}
\ No newline at end of file