mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-12 13:28:33 +00:00
UI - Tweaked monospace font sizing
This commit is contained in:
parent
2ee170b803
commit
fcde1d4ecc
@ -67,6 +67,7 @@
|
||||
<TextBlock Grid.Column="1"
|
||||
Text="{CompiledBinding DisplayValue}"
|
||||
FontFamily="{StaticResource RobotoMono}"
|
||||
FontSize="13"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="0 0 10 0" />
|
||||
</Grid>
|
||||
@ -81,7 +82,7 @@
|
||||
IsVisible="{CompiledBinding IsEventPicker, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type dataModelPicker:DataModelPicker}}}"/>
|
||||
</StackPanel>
|
||||
|
||||
<ContentControl Grid.Column="1" Content="{CompiledBinding DisplayViewModel}" FontFamily="{StaticResource RobotoMono}" Margin="0 0 10 0" />
|
||||
<ContentControl Grid.Column="1" Content="{CompiledBinding DisplayViewModel}" FontFamily="{StaticResource RobotoMono}" FontSize="13" Margin="0 0 10 0" />
|
||||
</Grid>
|
||||
</TreeDataTemplate>
|
||||
|
||||
@ -91,6 +92,7 @@
|
||||
<TextBlock Grid.Column="1"
|
||||
Text="{CompiledBinding CountDisplay, Mode=OneWay}"
|
||||
FontFamily="{StaticResource RobotoMono}"
|
||||
FontSize="13"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="0 0 10 0" />
|
||||
</Grid>
|
||||
|
||||
@ -52,8 +52,8 @@
|
||||
<TextBlock Grid.Column="1" Text="{CompiledBinding PropertyDescription.Name}" ToolTip.Tip="{CompiledBinding PropertyDescription.Description}" />
|
||||
<TextBlock Grid.Column="2"
|
||||
Text="{CompiledBinding DisplayValue}"
|
||||
FontSize="13"
|
||||
FontFamily="{StaticResource RobotoMono}"
|
||||
FontSize="13"
|
||||
HorizontalAlignment="Right" />
|
||||
</Grid>
|
||||
|
||||
|
||||
@ -10,8 +10,8 @@
|
||||
<SelectableTextBlock
|
||||
Inlines="{CompiledBinding Lines}"
|
||||
FontFamily="{StaticResource RobotoMono}"
|
||||
FontSize="12"
|
||||
SizeChanged="Control_OnSizeChanged"
|
||||
SelectionBrush="{StaticResource TextControlSelectionHighlightColor}"
|
||||
/>
|
||||
SelectionBrush="{StaticResource TextControlSelectionHighlightColor}"/>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
||||
@ -20,6 +20,7 @@
|
||||
<SelectableTextBlock
|
||||
Inlines="{CompiledBinding Lines}"
|
||||
FontFamily="{StaticResource RobotoMono}"
|
||||
FontSize="12"
|
||||
SizeChanged="Control_OnSizeChanged"
|
||||
SelectionBrush="{StaticResource TextControlSelectionHighlightColor}" />
|
||||
</ScrollViewer>
|
||||
|
||||
@ -41,7 +41,8 @@
|
||||
Text="{CompiledBinding Converter={StaticResource SKColorToStringConverter}, Mode=OneWay}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Stretch"
|
||||
FontFamily="{StaticResource RobotoMono}" />
|
||||
FontFamily="{StaticResource RobotoMono}"
|
||||
FontSize="13"/>
|
||||
<Border Margin="5 0 0 0"
|
||||
VerticalAlignment="Bottom"
|
||||
HorizontalAlignment="Right"
|
||||
@ -61,16 +62,16 @@
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="core:ColorGradient">
|
||||
<TextBlock Text="Color gradient" FontFamily="{StaticResource RobotoMono}" />
|
||||
<TextBlock Text="Color gradient" FontFamily="{StaticResource RobotoMono}" FontSize="13"/>
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="core:Numeric">
|
||||
<TextBlock Text="{CompiledBinding Mode=OneWay}" FontFamily="{StaticResource RobotoMono}" />
|
||||
<TextBlock Text="{CompiledBinding Mode=OneWay}" FontFamily="{StaticResource RobotoMono}" FontSize="13"/>
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="collections:IList">
|
||||
<TextBlock Text="{CompiledBinding Count, StringFormat='List - {0} item(s)', Mode=OneWay}" FontFamily="{StaticResource RobotoMono}" />
|
||||
<TextBlock Text="{CompiledBinding Count, StringFormat='List - {0} item(s)', Mode=OneWay}" FontFamily="{StaticResource RobotoMono}" FontSize="13"/>
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="system:Object">
|
||||
<TextBlock Text="{CompiledBinding Mode=OneWay}" FontFamily="{StaticResource RobotoMono}" />
|
||||
<TextBlock Text="{CompiledBinding Mode=OneWay}" FontFamily="{StaticResource RobotoMono}" FontSize="13"/>
|
||||
</DataTemplate>
|
||||
</ContentControl.DataTemplates>
|
||||
</ContentControl>
|
||||
|
||||
@ -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">
|
||||
<Grid RowDefinitions="Auto,*,Auto">
|
||||
<Grid RowDefinitions="Auto,Auto,*,Auto">
|
||||
<StackPanel>
|
||||
<StackPanel.Styles>
|
||||
<Styles>
|
||||
@ -25,8 +25,8 @@
|
||||
</Styles>
|
||||
</StackPanel.Styles>
|
||||
|
||||
<Grid ColumnDefinitions="103,*">
|
||||
<StackPanel Grid.Column="0" Width="95">
|
||||
<Grid ColumnDefinitions="105,*">
|
||||
<StackPanel Grid.Column="0" Width="95" HorizontalAlignment="Left">
|
||||
<Label Target="Name" Margin="0">Icon</Label>
|
||||
<Button Width="95"
|
||||
Height="95"
|
||||
@ -92,12 +92,22 @@
|
||||
|
||||
<Label>Tags</Label>
|
||||
<tagsInput:TagsInput Tags="{CompiledBinding Tags}" />
|
||||
|
||||
<Label Target="DescriptionEditor" Margin="0 28 0 0">Description</Label>
|
||||
</StackPanel>
|
||||
|
||||
<Grid Row="1" ColumnDefinitions="Auto,*">
|
||||
<Label Grid.Column="0" Target="DescriptionEditor" Margin="0 28 0 0">Description</Label>
|
||||
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<CheckBox Name="SynchronizedScrolling" IsChecked="True" VerticalAlignment="Bottom">Synchronized scrolling</CheckBox>
|
||||
<controls:HyperlinkButton
|
||||
Margin="0 0 0 -20"
|
||||
Content="Markdown supported"
|
||||
NavigateUri="https://wiki.artemis-rgb.com/guides/user/markdown?mtm_campaign=artemis&mtm_kwd=markdown-editor"
|
||||
HorizontalAlignment="Right"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
|
||||
<Grid Grid.Row="1" ColumnDefinitions="*,Auto,*">
|
||||
<Grid Grid.Row="2" ColumnDefinitions="*,Auto,*">
|
||||
<Border Grid.Column="0" BorderThickness="1"
|
||||
BorderBrush="{DynamicResource TextControlBorderBrush}"
|
||||
CornerRadius="{DynamicResource ControlCornerRadius}"
|
||||
@ -105,6 +115,7 @@
|
||||
Padding="{DynamicResource TextControlThemePadding}">
|
||||
<avaloniaEdit:TextEditor
|
||||
FontFamily="{StaticResource RobotoMono}"
|
||||
FontSize="13"
|
||||
Name="DescriptionEditor"
|
||||
Document="{CompiledBinding MarkdownDocument}"
|
||||
WordWrap="True" />
|
||||
@ -123,17 +134,12 @@
|
||||
</mdxaml:MarkdownScrollViewer>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
|
||||
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<CheckBox Name="SynchronizedScrolling" IsChecked="True" VerticalAlignment="Bottom">Synchronized scrolling</CheckBox>
|
||||
<controls:HyperlinkButton
|
||||
Margin="0 5 0 0"
|
||||
Content="Learn more about Markdown on the wiki"
|
||||
NavigateUri="https://wiki.artemis-rgb.com/guides/user/markdown?mtm_campaign=artemis&mtm_kwd=markdown-editor"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top" />
|
||||
</StackPanel>
|
||||
|
||||
|
||||
<TextBlock Grid.Row="3"
|
||||
Foreground="{DynamicResource SystemFillColorCriticalBrush}"
|
||||
Margin="2 8 0 0"
|
||||
IsVisible="{CompiledBinding !DescriptionValid}">
|
||||
A description is required
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@ -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<bool> _categoriesValid;
|
||||
private readonly ObservableAsPropertyHelper<bool> _iconValid;
|
||||
private readonly ObservableAsPropertyHelper<bool> _descriptionValid;
|
||||
private readonly IWorkshopClient _workshopClient;
|
||||
private readonly IWindowService _windowService;
|
||||
private ObservableAsPropertyHelper<bool>? _categoriesValid;
|
||||
private ObservableAsPropertyHelper<bool>? _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<int> 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<Unit, Unit> SelectIcon { 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;
|
||||
public ReadOnlyObservableCollection<int> 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<int> PreselectedCategories { get; set; } = new List<int>();
|
||||
|
||||
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<int> 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<IGetCategoriesResult> result)
|
||||
private async Task PopulateCategories()
|
||||
{
|
||||
IOperationResult<IGetCategoriesResult> 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)}));
|
||||
}
|
||||
}
|
||||
@ -12,8 +12,8 @@
|
||||
<UserControl.Resources>
|
||||
<converters:DateTimeConverter x:Key="DateTimeConverter" />
|
||||
</UserControl.Resources>
|
||||
<Grid ColumnDefinitions="300,*">
|
||||
<StackPanel Grid.Column="0" Spacing="10">
|
||||
<Grid ColumnDefinitions="300,*" RowDefinitions="*, Auto">
|
||||
<StackPanel Grid.Column="0" Grid.RowSpan="2" Spacing="10">
|
||||
<Border Classes="card" VerticalAlignment="Top" Margin="0 0 10 0">
|
||||
<StackPanel>
|
||||
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}">Management</TextBlock>
|
||||
@ -48,7 +48,11 @@
|
||||
View workshop page
|
||||
</controls:HyperlinkButton>
|
||||
</StackPanel>
|
||||
<ContentControl Grid.Column="1" Content="{CompiledBinding EntrySpecificationsViewModel}"></ContentControl>
|
||||
<ContentControl Grid.Column="1" Grid.Row="0" Content="{CompiledBinding EntrySpecificationsViewModel}"></ContentControl>
|
||||
<StackPanel Grid.Column="1" Grid.Row="1" HorizontalAlignment="Right" Spacing="5" Orientation="Horizontal" Margin="0 10 0 0">
|
||||
<Button Command="{CompiledBinding DiscardChanges}">Discard changes</Button>
|
||||
<Button Command="{CompiledBinding SaveChanges}">Save</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
</UserControl>
|
||||
@ -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<WorkshopDetailParameters>
|
||||
{
|
||||
private readonly IWorkshopClient _client;
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private readonly Func<EntrySpecificationsViewModel> _getEntrySpecificationsViewModel;
|
||||
private readonly Func<EntrySpecificationsViewModel> _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> entrySpecificationsViewModel)
|
||||
{
|
||||
public SubmissionDetailViewModel(IWorkshopClient client, IWindowService windowService, IWorkshopService workshopService, IRouter router, Func<EntrySpecificationsViewModel> 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<Unit, Unit> Save { get; }
|
||||
public ReactiveCommand<Unit, Unit> CreateRelease { get; }
|
||||
public ReactiveCommand<Unit, Unit> DeleteSubmission { get; }
|
||||
public ReactiveCommand<Unit, Unit> ViewWorkshopPage { get; }
|
||||
public ReactiveCommand<Unit, Unit> SaveChanges { get; }
|
||||
public ReactiveCommand<Unit, Unit> DiscardChanges { get; }
|
||||
|
||||
public EntrySpecificationsViewModel? EntrySpecificationsViewModel
|
||||
{
|
||||
@ -65,8 +66,12 @@ public class SubmissionDetailViewModel : RoutableScreen<WorkshopDetailParameters
|
||||
set => RaiseAndSetIfChanged(ref _entry, value);
|
||||
}
|
||||
|
||||
public ReactiveCommand<Unit, Unit> 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<IGetSubmittedEntryByIdResult> result = await _client.GetSubmittedEntryById.ExecuteAsync(parameters.EntryId, cancellationToken);
|
||||
@ -77,12 +82,29 @@ public class SubmissionDetailViewModel : RoutableScreen<WorkshopDetailParameters
|
||||
await ApplyFromEntry(cancellationToken);
|
||||
}
|
||||
|
||||
public override async Task OnClosing(NavigationArguments args)
|
||||
{
|
||||
if (!HasChanges)
|
||||
return;
|
||||
|
||||
bool confirmed = await _windowService.ShowConfirmContentDialog("You have unsaved changes", "Do you want to discard your unsaved changes?");
|
||||
if (!confirmed)
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
private async Task ApplyFromEntry(CancellationToken cancellationToken)
|
||||
{
|
||||
if (Entry == null)
|
||||
return;
|
||||
|
||||
EntrySpecificationsViewModel viewModel = _getEntrySpecificationsViewModel();
|
||||
if (EntrySpecificationsViewModel != null)
|
||||
{
|
||||
EntrySpecificationsViewModel.PropertyChanged -= EntrySpecificationsViewModelOnPropertyChanged;
|
||||
((INotifyCollectionChanged) EntrySpecificationsViewModel.SelectedCategories).CollectionChanged -= SelectedCategoriesOnCollectionChanged;
|
||||
EntrySpecificationsViewModel.Tags.CollectionChanged -= TagsOnCollectionChanged;
|
||||
}
|
||||
|
||||
EntrySpecificationsViewModel viewModel = _getGetSpecificationsVm();
|
||||
|
||||
viewModel.IconBitmap = await GetEntryIcon(cancellationToken);
|
||||
viewModel.Name = Entry.Name;
|
||||
@ -95,6 +117,9 @@ public class SubmissionDetailViewModel : RoutableScreen<WorkshopDetailParameters
|
||||
viewModel.Tags.Add(tag);
|
||||
|
||||
EntrySpecificationsViewModel = viewModel;
|
||||
EntrySpecificationsViewModel.PropertyChanged += EntrySpecificationsViewModelOnPropertyChanged;
|
||||
((INotifyCollectionChanged) EntrySpecificationsViewModel.SelectedCategories).CollectionChanged += SelectedCategoriesOnCollectionChanged;
|
||||
EntrySpecificationsViewModel.Tags.CollectionChanged += TagsOnCollectionChanged;
|
||||
}
|
||||
|
||||
private async Task<Bitmap?> GetEntryIcon(CancellationToken cancellationToken)
|
||||
@ -102,26 +127,59 @@ public class SubmissionDetailViewModel : RoutableScreen<WorkshopDetailParameters
|
||||
if (Entry == null)
|
||||
return null;
|
||||
|
||||
HttpClient client = _httpClientFactory.CreateClient(WorkshopConstants.WORKSHOP_CLIENT_NAME);
|
||||
try
|
||||
{
|
||||
HttpResponseMessage response = await client.GetAsync($"entries/{Entry.Id}/icon", cancellationToken);
|
||||
response.EnsureSuccessStatusCode();
|
||||
Stream data = await response.Content.ReadAsStreamAsync(cancellationToken);
|
||||
return new Bitmap(data);
|
||||
}
|
||||
catch (HttpRequestException)
|
||||
{
|
||||
// ignored
|
||||
return null;
|
||||
}
|
||||
Stream? stream = await _workshopService.GetEntryIcon(Entry.Id, cancellationToken);
|
||||
return stream != null ? new Bitmap(stream) : null;
|
||||
}
|
||||
|
||||
private async Task ExecuteSave(CancellationToken cancellationToken)
|
||||
private void UpdateHasChanges()
|
||||
{
|
||||
UpdateEntryInput input = new();
|
||||
if (EntrySpecificationsViewModel == null || Entry == null)
|
||||
return;
|
||||
|
||||
List<int> categories = EntrySpecificationsViewModel.Categories.Where(c => c.IsSelected).Select(c => c.Id).OrderBy(c => c).ToList();
|
||||
List<string> 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<IUpdateEntryResult> 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<StreamProgress>(), 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<WorkshopDetailParameters
|
||||
if (Entry != null)
|
||||
await _workshopService.NavigateToEntry(Entry.Id, Entry.EntryType);
|
||||
}
|
||||
|
||||
private void TagsOnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
UpdateHasChanges();
|
||||
}
|
||||
|
||||
private void SelectedCategoriesOnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
UpdateHasChanges();
|
||||
}
|
||||
|
||||
private void EntrySpecificationsViewModelOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
UpdateHasChanges();
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,4 @@
|
||||
using System.Reactive;
|
||||
using System.Reactive.Linq;
|
||||
using Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.Profile;
|
||||
using Artemis.WebClient.Workshop;
|
||||
using ReactiveUI;
|
||||
|
||||
@ -23,12 +21,6 @@ public class EntryTypeStepViewModel : SubmissionViewModel
|
||||
set => RaiseAndSetIfChanged(ref _selectedEntryType, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ReactiveCommand<Unit, Unit> Continue { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ReactiveCommand<Unit, Unit> GoBack { get; }
|
||||
|
||||
private void ExecuteContinue()
|
||||
{
|
||||
if (SelectedEntryType == null)
|
||||
|
||||
@ -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";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ReactiveCommand<Unit, Unit> Continue { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ReactiveCommand<Unit, Unit> GoBack { get; } = null!;
|
||||
|
||||
private async Task ExecuteLogin(CancellationToken ct)
|
||||
{
|
||||
ContentDialogResult result = await _windowService.CreateContentDialog().WithViewModel(out WorkshopLoginViewModel _).WithTitle("Workshop login").ShowAsync();
|
||||
|
||||
@ -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<string> _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<Unit, Unit> EditAdaptionHints { get; }
|
||||
|
||||
public int AdaptionHintCount
|
||||
|
||||
@ -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<ProfileAdaptionHintsLayerViewModel> _layers;
|
||||
private readonly IProfileService _profileService;
|
||||
private readonly IWindowService _windowService;
|
||||
|
||||
public ProfileAdaptionHintsStepViewModel(IWindowService windowService, IProfileService profileService, Func<Layer, ProfileAdaptionHintsLayerViewModel> 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<Unit, Unit> Continue { get; }
|
||||
public override ReactiveCommand<Unit, Unit> GoBack { get; }
|
||||
public ReactiveCommand<Layer, Unit> EditAdaptionHints { get; }
|
||||
public ReadOnlyObservableCollection<ProfileAdaptionHintsLayerViewModel> Layers { get; }
|
||||
|
||||
@ -61,7 +56,7 @@ public class ProfileAdaptionHintsStepViewModel : SubmissionViewModel
|
||||
{
|
||||
if (Layers.Any(l => l.AdaptionHintCount == 0))
|
||||
return;
|
||||
|
||||
|
||||
if (State.EntryId == null)
|
||||
State.ChangeScreen<SpecificationsStepViewModel>();
|
||||
else
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ReactiveCommand<Unit, Unit> Continue { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ReactiveCommand<Unit, Unit> GoBack { get; }
|
||||
|
||||
private void Update(ProfileConfiguration? profileConfiguration)
|
||||
{
|
||||
ProfilePreview.ProfileConfiguration = null;
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
|
||||
@ -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<EntrySpecificationsViewModel> _getEntrySpecificationsViewModel;
|
||||
private EntrySpecificationsViewModel? _entrySpecificationsViewModel;
|
||||
|
||||
public SpecificationsStepViewModel(Func<EntrySpecificationsViewModel> 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<Unit, Unit> Continue { get; }
|
||||
public override ReactiveCommand<Unit, Unit> 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<SubmitStepViewModel>();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
|
||||
@ -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<SpecificationsStepViewModel>());
|
||||
Continue = ReactiveCommand.Create(() => State.ChangeScreen<UploadStepViewModel>());
|
||||
|
||||
|
||||
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<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)))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,3 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ReactiveCommand<Unit, Unit> Continue { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ReactiveCommand<Unit, Unit> GoBack { get; } = null!;
|
||||
|
||||
public int ProgressPercentage => _progressPercentage.Value;
|
||||
public bool ProgressIndeterminate => _progressIndeterminate.Value;
|
||||
|
||||
|
||||
@ -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
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ReactiveCommand<Unit, Unit> Continue { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ReactiveCommand<Unit, Unit> GoBack { get; } = null!;
|
||||
|
||||
public ReactiveCommand<Unit, Unit> Refresh { get; }
|
||||
public ReactiveCommand<Unit, Process?> Resend { get; }
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ReactiveCommand<Unit, Unit> Continue { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ReactiveCommand<Unit, Unit> GoBack { get; } = null!;
|
||||
|
||||
private async Task ExecuteContinue()
|
||||
{
|
||||
bool loggedIn = await _authenticationService.AutoLogin(true);
|
||||
|
||||
@ -6,15 +6,26 @@ namespace Artemis.UI.Screens.Workshop.SubmissionWizard;
|
||||
|
||||
public abstract class SubmissionViewModel : ValidatableViewModelBase
|
||||
{
|
||||
private ReactiveCommand<Unit, Unit>? _continue;
|
||||
private ReactiveCommand<Unit, Unit>? _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<Unit, Unit> Continue { get; }
|
||||
public abstract ReactiveCommand<Unit, Unit> GoBack { get; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit>? Continue
|
||||
{
|
||||
get => _continue;
|
||||
set => RaiseAndSetIfChanged(ref _continue, value);
|
||||
}
|
||||
|
||||
public ReactiveCommand<Unit, Unit>? GoBack
|
||||
{
|
||||
get => _goBack;
|
||||
set => RaiseAndSetIfChanged(ref _goBack, value);
|
||||
}
|
||||
|
||||
public bool ShowHeader
|
||||
{
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="system:Object">
|
||||
<Border Classes="card-condensed" Margin="0,5,5,5 ">
|
||||
<TextBlock Text="{CompiledBinding Converter={StaticResource JsonConverter}}" FontFamily="{StaticResource RobotoMono}"/>
|
||||
<TextBlock Text="{CompiledBinding Converter={StaticResource JsonConverter}}" FontFamily="{StaticResource RobotoMono}" FontSize="12"/>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ContentControl.DataTemplates>
|
||||
|
||||
@ -16,6 +16,22 @@ public class WorkshopService : IWorkshopService
|
||||
_router = router;
|
||||
}
|
||||
|
||||
public async Task<Stream?> 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<ImageUploadResult> SetEntryIcon(Guid entryId, Progress<StreamProgress> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<bool> ValidateWorkshopStatus(CancellationToken cancellationToken)
|
||||
{
|
||||
@ -86,10 +102,11 @@ public class WorkshopService : IWorkshopService
|
||||
|
||||
public interface IWorkshopService
|
||||
{
|
||||
Task<Stream?> GetEntryIcon(Guid entryId, CancellationToken cancellationToken);
|
||||
Task<ImageUploadResult> SetEntryIcon(Guid entryId, Progress<StreamProgress> progress, Stream icon, CancellationToken cancellationToken);
|
||||
Task<WorkshopStatus> GetWorkshopStatus(CancellationToken cancellationToken);
|
||||
Task<bool> ValidateWorkshopStatus(CancellationToken cancellationToken);
|
||||
Task NavigateToEntry(Guid entryId, EntryType entryType);
|
||||
|
||||
|
||||
public record WorkshopStatus(bool IsReachable, string Message);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user