mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-12 03:48:56 +00:00
Workshop - Added profile uploading
Submission wizard - Final upload steps
This commit is contained in:
1 parent
e52faf1c9f
commit
ad4da3032d
32 files changed
+621
-93
No files matched your search
+2
-2
@@ -6,8 +6,8 @@
|
||||
xmlns:workshop="clr-namespace:Artemis.WebClient.Workshop;assembly=Artemis.WebClient.Workshop"
|
||||
xmlns:converters="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.EntryTypeView"
|
||||
x:DataType="steps:EntryTypeViewModel">
|
||||
x:Class="Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.EntryTypeStepView"
|
||||
x:DataType="steps:EntryTypeStepViewModel">
|
||||
<UserControl.Resources>
|
||||
<converters:EnumToBooleanConverter x:Key="EnumBoolConverter" />
|
||||
</UserControl.Resources>
|
||||
+2
-2
@@ -2,9 +2,9 @@ using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Workshop.SubmissionWizard.Steps;
|
||||
|
||||
public partial class EntryTypeView : ReactiveUserControl<EntryTypeViewModel>
|
||||
public partial class EntryTypeStepView : ReactiveUserControl<EntryTypeStepViewModel>
|
||||
{
|
||||
public EntryTypeView()
|
||||
public EntryTypeStepView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
+2
-2
@@ -6,12 +6,12 @@ using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Workshop.SubmissionWizard.Steps;
|
||||
|
||||
public class EntryTypeViewModel : SubmissionViewModel
|
||||
public class EntryTypeStepViewModel : SubmissionViewModel
|
||||
{
|
||||
private EntryType? _selectedEntryType;
|
||||
|
||||
/// <inheritdoc />
|
||||
public EntryTypeViewModel()
|
||||
public EntryTypeStepViewModel()
|
||||
{
|
||||
GoBack = ReactiveCommand.Create(() => State.ChangeScreen<WelcomeStepViewModel>());
|
||||
Continue = ReactiveCommand.Create(ExecuteContinue, this.WhenAnyValue(vm => vm.SelectedEntryType).Select(e => e != null));
|
||||
@@ -43,7 +43,7 @@ public class LoginStepViewModel : SubmissionViewModel
|
||||
|
||||
Claim? emailVerified = _authenticationService.Claims.FirstOrDefault(c => c.Type == JwtClaimTypes.EmailVerified);
|
||||
if (emailVerified?.Value == "true")
|
||||
State.ChangeScreen<EntryTypeViewModel>();
|
||||
State.ChangeScreen<EntryTypeStepViewModel>();
|
||||
else
|
||||
State.ChangeScreen<ValidateEmailStepViewModel>();
|
||||
}
|
||||
|
||||
+1
-1
@@ -62,6 +62,6 @@ public class ProfileAdaptionHintsStepViewModel : SubmissionViewModel
|
||||
if (Layers.Any(l => l.AdaptionHintCount == 0))
|
||||
return;
|
||||
|
||||
State.ChangeScreen<EntrySpecificationsStepViewModel>();
|
||||
State.ChangeScreen<SpecificationsStepViewModel>();
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -43,7 +43,7 @@ public class ProfileSelectionStepViewModel : SubmissionViewModel
|
||||
|
||||
ProfilePreview = profilePreviewViewModel;
|
||||
|
||||
GoBack = ReactiveCommand.Create(() => State.ChangeScreen<EntryTypeViewModel>());
|
||||
GoBack = ReactiveCommand.Create(() => State.ChangeScreen<EntryTypeStepViewModel>());
|
||||
Continue = ReactiveCommand.Create(ExecuteContinue, this.WhenAnyValue(vm => vm.SelectedProfile).Select(p => p != null));
|
||||
|
||||
this.WhenAnyValue(vm => vm.SelectedProfile).Subscribe(p => Update(p));
|
||||
|
||||
+2
-2
@@ -7,8 +7,8 @@
|
||||
xmlns:categories="clr-namespace:Artemis.UI.Screens.Workshop.Categories"
|
||||
xmlns:tagsInput="clr-namespace:Artemis.UI.Shared.TagsInput;assembly=Artemis.UI.Shared"
|
||||
mc:Ignorable="d" d:DesignWidth="970" d:DesignHeight="625"
|
||||
x:Class="Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.EntrySpecificationsStepView"
|
||||
x:DataType="steps:EntrySpecificationsStepViewModel">
|
||||
x:Class="Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.SpecificationsStepView"
|
||||
x:DataType="steps:SpecificationsStepViewModel">
|
||||
<Grid RowDefinitions="Auto,*">
|
||||
<StackPanel Grid.Row="0">
|
||||
<StackPanel.Styles>
|
||||
+2
-2
@@ -5,9 +5,9 @@ using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Workshop.SubmissionWizard.Steps;
|
||||
|
||||
public partial class EntrySpecificationsStepView : ReactiveUserControl<EntrySpecificationsStepViewModel>
|
||||
public partial class SpecificationsStepView : ReactiveUserControl<SpecificationsStepViewModel>
|
||||
{
|
||||
public EntrySpecificationsStepView()
|
||||
public SpecificationsStepView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
+70
-74
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
@@ -13,7 +12,6 @@ using Artemis.UI.Screens.Workshop.Categories;
|
||||
using Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.Profile;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Artemis.WebClient.Workshop;
|
||||
using Artemis.WebClient.Workshop.Extensions;
|
||||
using Avalonia.Threading;
|
||||
using DynamicData;
|
||||
using DynamicData.Aggregation;
|
||||
@@ -26,18 +24,17 @@ using Bitmap = Avalonia.Media.Imaging.Bitmap;
|
||||
|
||||
namespace Artemis.UI.Screens.Workshop.SubmissionWizard.Steps;
|
||||
|
||||
public class EntrySpecificationsStepViewModel : SubmissionViewModel
|
||||
public class SpecificationsStepViewModel : SubmissionViewModel
|
||||
{
|
||||
private readonly IWindowService _windowService;
|
||||
private ObservableAsPropertyHelper<bool>? _categoriesValid;
|
||||
private ObservableAsPropertyHelper<bool>? _iconValid;
|
||||
private string _description = string.Empty;
|
||||
private Bitmap? _iconBitmap;
|
||||
private bool _isDirty;
|
||||
private string _name = string.Empty;
|
||||
private string _summary = string.Empty;
|
||||
private Bitmap? _iconBitmap;
|
||||
|
||||
public EntrySpecificationsStepViewModel(IWorkshopClient workshopClient, IWindowService windowService)
|
||||
public SpecificationsStepViewModel(IWorkshopClient workshopClient, IWindowService windowService)
|
||||
{
|
||||
_windowService = windowService;
|
||||
GoBack = ReactiveCommand.Create(ExecuteGoBack);
|
||||
@@ -45,47 +42,18 @@ public class EntrySpecificationsStepViewModel : SubmissionViewModel
|
||||
SelectIcon = ReactiveCommand.CreateFromTask(ExecuteSelectIcon);
|
||||
ClearIcon = ReactiveCommand.Create(ExecuteClearIcon);
|
||||
|
||||
workshopClient.GetCategories
|
||||
.Watch(ExecutionStrategy.CacheFirst)
|
||||
.SelectOperationResult(c => c.Categories)
|
||||
.ToObservableChangeSet(c => c.Id)
|
||||
.Transform(c => new CategoryViewModel(c))
|
||||
.Bind(out ReadOnlyObservableCollection<CategoryViewModel> categoryViewModels)
|
||||
.Subscribe();
|
||||
Categories = categoryViewModels;
|
||||
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
DisplayName = $"{State.EntryType} Information";
|
||||
|
||||
// Basic fields
|
||||
Name = State.Name;
|
||||
Summary = State.Summary;
|
||||
Description = State.Description;
|
||||
|
||||
// Categories
|
||||
foreach (CategoryViewModel categoryViewModel in Categories)
|
||||
categoryViewModel.IsSelected = State.Categories.Contains(categoryViewModel.Id);
|
||||
// Load categories
|
||||
Observable.FromAsync(workshopClient.GetCategories.ExecuteAsync).Subscribe(PopulateCategories).DisposeWith(d);
|
||||
|
||||
// Tags
|
||||
Tags.Clear();
|
||||
Tags.AddRange(State.Tags);
|
||||
// Apply the state
|
||||
ApplyFromState();
|
||||
|
||||
// Icon
|
||||
if (State.Icon != null)
|
||||
{
|
||||
State.Icon.Seek(0, SeekOrigin.Begin);
|
||||
IconBitmap = BitmapExtensions.LoadAndResize(State.Icon, 128);
|
||||
}
|
||||
|
||||
IsDirty = false;
|
||||
this.ClearValidationRules();
|
||||
|
||||
Disposable.Create(() =>
|
||||
{
|
||||
IconBitmap?.Dispose();
|
||||
IconBitmap = null;
|
||||
}).DisposeWith(d);
|
||||
Disposable.Create(ExecuteClearIcon).DisposeWith(d);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -94,7 +62,7 @@ public class EntrySpecificationsStepViewModel : SubmissionViewModel
|
||||
public ReactiveCommand<Unit, Unit> SelectIcon { get; }
|
||||
public ReactiveCommand<Unit, Unit> ClearIcon { get; }
|
||||
|
||||
public ReadOnlyObservableCollection<CategoryViewModel> Categories { get; }
|
||||
public ObservableCollection<CategoryViewModel> Categories { get; } = new();
|
||||
public ObservableCollection<string> Tags { get; } = new();
|
||||
public bool CategoriesValid => _categoriesValid?.Value ?? true;
|
||||
public bool IconValid => _iconValid?.Value ?? true;
|
||||
@@ -117,12 +85,6 @@ public class EntrySpecificationsStepViewModel : SubmissionViewModel
|
||||
set => RaiseAndSetIfChanged(ref _description, value);
|
||||
}
|
||||
|
||||
public bool IsDirty
|
||||
{
|
||||
get => _isDirty;
|
||||
set => RaiseAndSetIfChanged(ref _isDirty, value);
|
||||
}
|
||||
|
||||
public Bitmap? IconBitmap
|
||||
{
|
||||
get => _iconBitmap;
|
||||
@@ -131,6 +93,9 @@ public class EntrySpecificationsStepViewModel : SubmissionViewModel
|
||||
|
||||
private void ExecuteGoBack()
|
||||
{
|
||||
// Apply what's there so far
|
||||
ApplyToState();
|
||||
|
||||
switch (State.EntryType)
|
||||
{
|
||||
case EntryType.Layout:
|
||||
@@ -147,35 +112,20 @@ public class EntrySpecificationsStepViewModel : SubmissionViewModel
|
||||
|
||||
private void ExecuteContinue()
|
||||
{
|
||||
if (!IsDirty)
|
||||
if (!ValidationContext.Validations.Any())
|
||||
{
|
||||
SetupDataValidation();
|
||||
IsDirty = true;
|
||||
|
||||
// The ValidationContext seems to update asynchronously, so stop and schedule a retry
|
||||
SetupDataValidation();
|
||||
Dispatcher.UIThread.Post(ExecuteContinue);
|
||||
return;
|
||||
}
|
||||
|
||||
ApplyToState();
|
||||
|
||||
if (!ValidationContext.GetIsValid())
|
||||
return;
|
||||
|
||||
State.Name = Name;
|
||||
State.Summary = Summary;
|
||||
State.Description = Description;
|
||||
State.Categories = Categories.Where(c => c.IsSelected).Select(c => c.Id).ToList();
|
||||
State.Tags = new List<string>(Tags);
|
||||
|
||||
State.Icon?.Dispose();
|
||||
if (IconBitmap != null)
|
||||
{
|
||||
State.Icon = new MemoryStream();
|
||||
IconBitmap.Save(State.Icon);
|
||||
}
|
||||
else
|
||||
{
|
||||
State.Icon = null;
|
||||
}
|
||||
|
||||
State.ChangeScreen<SubmitStepViewModel>();
|
||||
}
|
||||
|
||||
private async Task ExecuteSelectIcon()
|
||||
@@ -197,6 +147,13 @@ public class EntrySpecificationsStepViewModel : SubmissionViewModel
|
||||
IconBitmap = null;
|
||||
}
|
||||
|
||||
private void PopulateCategories(IOperationResult<IGetCategoriesResult> result)
|
||||
{
|
||||
Categories.Clear();
|
||||
if (result.Data != null)
|
||||
Categories.AddRange(result.Data.Categories.Select(c => new CategoryViewModel(c) {IsSelected = State.Categories.Contains(c.Id)}));
|
||||
}
|
||||
|
||||
private void SetupDataValidation()
|
||||
{
|
||||
// Hopefully this can be avoided in the future
|
||||
@@ -204,17 +161,56 @@ public class EntrySpecificationsStepViewModel : SubmissionViewModel
|
||||
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");
|
||||
|
||||
|
||||
// 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)
|
||||
.IsEmpty()
|
||||
.CombineLatest(this.WhenAnyValue(vm => vm.IsDirty), (empty, dirty) => !dirty || !empty),
|
||||
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);
|
||||
}
|
||||
|
||||
private void ApplyFromState()
|
||||
{
|
||||
// Basic fields
|
||||
Name = State.Name;
|
||||
Summary = State.Summary;
|
||||
Description = State.Description;
|
||||
|
||||
// Tags
|
||||
Tags.Clear();
|
||||
Tags.AddRange(State.Tags);
|
||||
|
||||
// Icon
|
||||
if (State.Icon != null)
|
||||
{
|
||||
State.Icon.Seek(0, SeekOrigin.Begin);
|
||||
IconBitmap = BitmapExtensions.LoadAndResize(State.Icon, 128);
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyToState()
|
||||
{
|
||||
// Basic fields
|
||||
State.Name = Name;
|
||||
State.Summary = Summary;
|
||||
State.Description = Description;
|
||||
|
||||
// Categories and tasks
|
||||
State.Categories = Categories.Where(c => c.IsSelected).Select(c => c.Id).ToList();
|
||||
State.Tags = new List<string>(Tags);
|
||||
|
||||
// Icon
|
||||
State.Icon?.Dispose();
|
||||
if (IconBitmap != null)
|
||||
{
|
||||
State.Icon = new MemoryStream();
|
||||
IconBitmap.Save(State.Icon);
|
||||
}
|
||||
else
|
||||
{
|
||||
State.Icon = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
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:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||
xmlns:steps="clr-namespace:Artemis.UI.Screens.Workshop.SubmissionWizard.Steps"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.SubmitStepView"
|
||||
x:DataType="steps:SubmitStepViewModel">
|
||||
<StackPanel>
|
||||
<StackPanel.Styles>
|
||||
<Styles>
|
||||
<Style Selector="TextBlock">
|
||||
<Setter Property="TextWrapping" Value="Wrap"></Setter>
|
||||
</Style>
|
||||
</Styles>
|
||||
</StackPanel.Styles>
|
||||
<TextBlock Theme="{StaticResource TitleTextBlockStyle}">
|
||||
Ready to submit?
|
||||
</TextBlock>
|
||||
<TextBlock>
|
||||
We have all the information we need, are you ready to submit the following to the workshop?
|
||||
</TextBlock>
|
||||
|
||||
<Border Classes="card" Margin="0 15 0 0" >
|
||||
<Grid ColumnDefinitions="Auto,*">
|
||||
<!-- Icon -->
|
||||
<Border Grid.Column="0"
|
||||
CornerRadius="12"
|
||||
Background="{StaticResource ControlStrokeColorOnAccentDefault}"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0 0 10 0"
|
||||
Width="80"
|
||||
Height="80"
|
||||
ClipToBounds="True">
|
||||
<Image Source="{CompiledBinding IconBitmap}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"></Image>
|
||||
</Border>
|
||||
|
||||
<!-- Body -->
|
||||
<Grid Grid.Column="1" VerticalAlignment="Stretch" RowDefinitions="Auto,*,Auto">
|
||||
<TextBlock Grid.Row="0" Margin="0 0 0 5" TextTrimming="CharacterEllipsis" >
|
||||
<Run Classes="h5" Text="{CompiledBinding State.Name, FallbackValue=Title}" />
|
||||
<Run Classes="subtitle">by</Run>
|
||||
<Run Classes="subtitle" Text="{CompiledBinding CurrentUser, FallbackValue=Author}" />
|
||||
</TextBlock>
|
||||
<TextBlock Grid.Row="1"
|
||||
Classes="subtitle"
|
||||
TextWrapping="Wrap"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
Text="{CompiledBinding State.Summary, FallbackValue=Summary}"></TextBlock>
|
||||
|
||||
<ItemsControl Grid.Row="2" ItemsSource="{CompiledBinding Categories}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal" Spacing="8"></StackPanel>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<avalonia:MaterialIcon Kind="{CompiledBinding Icon}" Margin="0 0 3 0"></avalonia:MaterialIcon>
|
||||
<TextBlock Text="{CompiledBinding Name}" TextTrimming="CharacterEllipsis" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,19 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Workshop.SubmissionWizard.Steps;
|
||||
|
||||
public partial class SubmitStepView : ReactiveUserControl<SubmitStepViewModel>
|
||||
{
|
||||
public SubmitStepView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
using System.Collections.ObjectModel;
|
||||
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 IdentityModel;
|
||||
using ReactiveUI;
|
||||
using StrawberryShake;
|
||||
using System;
|
||||
using System.IO;
|
||||
using Avalonia.Media.Imaging;
|
||||
|
||||
namespace Artemis.UI.Screens.Workshop.SubmissionWizard.Steps;
|
||||
|
||||
public class SubmitStepViewModel : SubmissionViewModel
|
||||
{
|
||||
private ReadOnlyObservableCollection<CategoryViewModel>? _categories;
|
||||
private Bitmap? _iconBitmap;
|
||||
|
||||
/// <inheritdoc />
|
||||
public SubmitStepViewModel(IAuthenticationService authenticationService, IWorkshopClient workshopClient)
|
||||
{
|
||||
CurrentUser = authenticationService.Claims.FirstOrDefault(c => c.Type == JwtClaimTypes.Name)?.Value;
|
||||
GoBack = ReactiveCommand.Create(() => State.ChangeScreen<SpecificationsStepViewModel>());
|
||||
Continue = ReactiveCommand.Create(() => State.ChangeScreen<UploadStepViewModel>());
|
||||
|
||||
ContinueText = "Submit";
|
||||
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
if (State.Icon != null)
|
||||
{
|
||||
State.Icon.Seek(0, SeekOrigin.Begin);
|
||||
IconBitmap = new Bitmap(State.Icon);
|
||||
IconBitmap.DisposeWith(d);
|
||||
}
|
||||
Observable.FromAsync(workshopClient.GetCategories.ExecuteAsync).Subscribe(PopulateCategories).DisposeWith(d);
|
||||
});
|
||||
}
|
||||
|
||||
public Bitmap? IconBitmap
|
||||
{
|
||||
get => _iconBitmap;
|
||||
set => RaiseAndSetIfChanged(ref _iconBitmap, value);
|
||||
}
|
||||
|
||||
public string? CurrentUser { get; }
|
||||
|
||||
public ReadOnlyObservableCollection<CategoryViewModel>? Categories
|
||||
{
|
||||
get => _categories;
|
||||
set => RaiseAndSetIfChanged(ref _categories, value);
|
||||
}
|
||||
|
||||
public override ReactiveCommand<Unit, Unit> Continue { get; }
|
||||
|
||||
public override ReactiveCommand<Unit, Unit> GoBack { get; }
|
||||
|
||||
private void PopulateCategories(IOperationResult<IGetCategoriesResult> result)
|
||||
{
|
||||
if (result.Data == null)
|
||||
Categories = null;
|
||||
else
|
||||
{
|
||||
Categories = new ReadOnlyObservableCollection<CategoryViewModel>(
|
||||
new ObservableCollection<CategoryViewModel>(result.Data.Categories.Where(c => State.Categories.Contains(c.Id)).Select(c => new CategoryViewModel(c)))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
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:steps="clr-namespace:Artemis.UI.Screens.Workshop.SubmissionWizard.Steps"
|
||||
mc:Ignorable="d" d:DesignWidth="970" d:DesignHeight="900"
|
||||
x:Class="Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.UploadStepView"
|
||||
x:DataType="steps:UploadStepViewModel">
|
||||
<StackPanel Margin="0 50 0 0" >
|
||||
<StackPanel.Styles>
|
||||
<Styles>
|
||||
<Style Selector="TextBlock">
|
||||
<Setter Property="TextAlignment" Value="Center"></Setter>
|
||||
<Setter Property="TextWrapping" Value="Wrap"></Setter>
|
||||
</Style>
|
||||
</Styles>
|
||||
</StackPanel.Styles>
|
||||
<TextBlock Theme="{StaticResource TitleTextBlockStyle}" TextAlignment="Center" TextWrapping="Wrap">
|
||||
Uploading your submission...
|
||||
</TextBlock>
|
||||
<TextBlock TextAlignment="Center" TextWrapping="Wrap">
|
||||
Wooo, the final step, that was pretty easy, right!?
|
||||
</TextBlock>
|
||||
|
||||
<StackPanel IsVisible="{CompiledBinding Finished}" Margin="0 100 0 0">
|
||||
<Lottie Path="/Assets/Animations/success.json" RepeatCount="1" Width="300" Height="300"></Lottie>
|
||||
<TextBlock Theme="{StaticResource TitleTextBlockStyle}">All done! Hit finish to view your submission.</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,19 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Workshop.SubmissionWizard.Steps;
|
||||
|
||||
public partial class UploadStepView : ReactiveUserControl<UploadStepViewModel>
|
||||
{
|
||||
public UploadStepView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Reactive;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Artemis.WebClient.Workshop;
|
||||
using Artemis.WebClient.Workshop.UploadHandlers;
|
||||
using ReactiveUI;
|
||||
using StrawberryShake;
|
||||
using System.Reactive.Disposables;
|
||||
using Artemis.Core;
|
||||
|
||||
namespace Artemis.UI.Screens.Workshop.SubmissionWizard.Steps;
|
||||
|
||||
public class UploadStepViewModel : SubmissionViewModel
|
||||
{
|
||||
private readonly IWorkshopClient _workshopClient;
|
||||
private readonly EntryUploadHandlerFactory _entryUploadHandlerFactory;
|
||||
private readonly IWindowService _windowService;
|
||||
private bool _finished;
|
||||
|
||||
/// <inheritdoc />
|
||||
public UploadStepViewModel(IWorkshopClient workshopClient, EntryUploadHandlerFactory entryUploadHandlerFactory, IWindowService windowService)
|
||||
{
|
||||
_workshopClient = workshopClient;
|
||||
_entryUploadHandlerFactory = entryUploadHandlerFactory;
|
||||
_windowService = windowService;
|
||||
|
||||
ShowGoBack = false;
|
||||
ContinueText = "Finish";
|
||||
Continue = ReactiveCommand.Create(ExecuteContinue, this.WhenAnyValue(vm => vm.Finished));
|
||||
|
||||
this.WhenActivated(d => Observable.FromAsync(ExecuteUpload).Subscribe().DisposeWith(d));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ReactiveCommand<Unit, Unit> Continue { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ReactiveCommand<Unit, Unit> GoBack { get; } = null!;
|
||||
|
||||
public bool Finished
|
||||
{
|
||||
get => _finished;
|
||||
set => RaiseAndSetIfChanged(ref _finished, value);
|
||||
}
|
||||
|
||||
public async Task ExecuteUpload(CancellationToken cancellationToken)
|
||||
{
|
||||
IOperationResult<IAddEntryResult> result = await _workshopClient.AddEntry.ExecuteAsync(new CreateEntryInput
|
||||
{
|
||||
EntryType = State.EntryType,
|
||||
Name = State.Name,
|
||||
Summary = State.Summary,
|
||||
Description = State.Description,
|
||||
Categories = State.Categories,
|
||||
Tags = State.Tags
|
||||
}, cancellationToken);
|
||||
|
||||
Guid? entryId = result.Data?.AddEntry?.Id;
|
||||
if (result.IsErrorResult() || entryId == null)
|
||||
{
|
||||
await _windowService.ShowConfirmContentDialog("Failed to create workshop entry", result.Errors.ToString() ?? "Not even an error message", "Close", null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
return;
|
||||
|
||||
// Create the workshop entry
|
||||
try
|
||||
{
|
||||
IEntryUploadHandler uploadHandler = _entryUploadHandlerFactory.CreateHandler(State.EntryType);
|
||||
EntryUploadResult uploadResult = await uploadHandler.CreateReleaseAsync(entryId.Value, State.EntrySource!, cancellationToken);
|
||||
if (!uploadResult.IsSuccess)
|
||||
{
|
||||
string? message = uploadResult.Message;
|
||||
if (message != null)
|
||||
message += "\r\n\r\n";
|
||||
else
|
||||
message = "";
|
||||
message += "Your submission has still been saved, you may try to upload a new release";
|
||||
await _windowService.ShowConfirmContentDialog("Failed to upload workshop entry", message, "Close", null);
|
||||
return;
|
||||
}
|
||||
|
||||
Finished = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// Something went wrong when creating a release :c
|
||||
// We'll keep the workshop entry so that the user can make changes and try again
|
||||
}
|
||||
}
|
||||
|
||||
private void ExecuteContinue()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -73,7 +73,7 @@ public class ValidateEmailStepViewModel : SubmissionViewModel
|
||||
|
||||
private void ExecuteContinue()
|
||||
{
|
||||
State.ChangeScreen<EntryTypeViewModel>();
|
||||
State.ChangeScreen<EntryTypeStepViewModel>();
|
||||
}
|
||||
|
||||
private async Task ExecuteRefresh(CancellationToken ct)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:steps="clr-namespace:Artemis.UI.Screens.Workshop.SubmissionWizard.Steps"
|
||||
mc:Ignorable="d" d:DesignWidth="970" d:DesignHeight="625"
|
||||
mc:Ignorable="d" d:DesignWidth="970" d:DesignHeight="900"
|
||||
x:Class="Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.WelcomeStepView"
|
||||
x:DataType="steps:WelcomeStepViewModel">
|
||||
<StackPanel Margin="0 50 0 0" >
|
||||
@@ -14,6 +14,6 @@
|
||||
Here we'll take you, step by step, through the process of uploading your submission to the workshop.
|
||||
</TextBlock>
|
||||
|
||||
<Lottie Path="/Assets/Animations/workshop-wizard.json" RepeatCount="1" Width="300" Height="400"></Lottie>
|
||||
<Lottie Path="/Assets/Animations/workshop-wizard.json" RepeatCount="1" Width="500" Height="700"></Lottie>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
@@ -38,7 +38,7 @@ public class WelcomeStepViewModel : SubmissionViewModel
|
||||
else
|
||||
{
|
||||
if (_authenticationService.Claims.Any(c => c.Type == JwtClaimTypes.EmailVerified && c.Value == "true"))
|
||||
State.ChangeScreen<EntryTypeViewModel>();
|
||||
State.ChangeScreen<EntryTypeStepViewModel>();
|
||||
else
|
||||
State.ChangeScreen<ValidateEmailStepViewModel>();
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
Icon="/Assets/Images/Logo/application.ico"
|
||||
Title="Artemis | Workshop submission wizard"
|
||||
Width="1000"
|
||||
Height="735"
|
||||
Height="950"
|
||||
WindowStartupLocation="CenterOwner">
|
||||
<Grid Margin="15" RowDefinitions="Auto,*,Auto">
|
||||
<Grid RowDefinitions="*,*" ColumnDefinitions="Auto,*,Auto" Margin="0 0 0 15">
|
||||
|
||||
Reference in new issue
Block a user