Workshop - Add button to upload submission directly from library

Workshop - While creating submission allow fitting the icon instead of cropping
Workshop - Add the ability to filter default entries
This commit is contained in:
Robert committed 2025-12-06 20:27:48 +01:00
1 parent 13c497f41e
commit f0cbc3e561
11 files changed
+227 -60

No files matched your search

@@ -187,7 +187,7 @@ public partial class ProfileConfigurationEditViewModel : DialogViewModelBase<Pro
if (result == null)
return;
SelectedBitmapSource = BitmapExtensions.LoadAndResize(result[0], 128);
SelectedBitmapSource = BitmapExtensions.LoadAndResize(result[0], 128, false);
_selectedIconPath = result[0];
}
@@ -51,11 +51,11 @@
ToolTip.Tip="Click to browse">
</Button>
</Panel>
</Border>
<TextBlock Foreground="{DynamicResource SystemFillColorCriticalBrush}" Margin="2 0" IsVisible="{CompiledBinding !IconValid}" TextWrapping="Wrap">
Icon required
</TextBlock>
<CheckBox IsChecked="{CompiledBinding Fit}">Shrink</CheckBox>
</StackPanel>
<StackPanel Grid.Column="1">
<Label Target="Name" Margin="0">Name</Label>
@@ -38,15 +38,18 @@ public partial class EntrySpecificationsViewModel : ValidatableViewModelBase
[Notify] private bool _isDefault;
[Notify] private bool _isEssential;
[Notify] private bool _isDeviceProvider;
[Notify] private bool _fit;
[Notify] private Bitmap? _iconBitmap;
[Notify(Setter.Private)] private bool _iconChanged;
private string? _lastIconPath;
public EntrySpecificationsViewModel(IWorkshopClient workshopClient, IWindowService windowService, IAuthenticationService authenticationService)
{
_workshopClient = workshopClient;
_windowService = windowService;
SelectIcon = ReactiveCommand.CreateFromTask(ExecuteSelectIcon);
Categories.ToObservableChangeSet()
.AutoRefresh(c => c.IsSelected)
.Filter(c => c.IsSelected)
@@ -68,23 +71,24 @@ public partial class EntrySpecificationsViewModel : ValidatableViewModelBase
_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 _ => await PopulateCategories());
IsAdministrator = authenticationService.GetRoles().Contains("Administrator");
this.WhenActivatedAsync(async _ => await PopulateCategories());
this.WhenAnyValue(vm => vm.Fit).Subscribe(_ => UpdateIcon());
}
public ReactiveCommand<Unit, Unit> SelectIcon { get; }
public ObservableCollection<CategoryViewModel> Categories { get; } = new();
public ObservableCollection<string> Tags { get; } = new();
public ReadOnlyObservableCollection<long> SelectedCategories { get; }
public bool CategoriesValid => _categoriesValid.Value ;
public bool CategoriesValid => _categoriesValid.Value;
public bool IconValid => _iconValid.Value;
public bool DescriptionValid => _descriptionValid.Value;
public bool IsAdministrator { get; }
public List<long> PreselectedCategories { get; set; } = new();
private async Task ExecuteSelectIcon()
{
string[]? result = await _windowService.CreateOpenFileDialog()
@@ -94,8 +98,17 @@ public partial class EntrySpecificationsViewModel : ValidatableViewModelBase
if (result == null)
return;
_lastIconPath = result[0];
UpdateIcon();
}
private void UpdateIcon()
{
if (_lastIconPath == null)
return;
IconBitmap?.Dispose();
IconBitmap = BitmapExtensions.LoadAndResize(result[0], 128);
IconBitmap = BitmapExtensions.LoadAndResize(_lastIconPath, 128, Fit);
IconChanged = true;
}
@@ -3,6 +3,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:list="clr-namespace:Artemis.UI.Screens.Workshop.Entries.List"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.Workshop.Entries.List.EntryListView"
x:DataType="list:EntryListViewModel">
@@ -26,6 +27,20 @@
HorizontalAlignment="Right"
Width="300"
IsVisible="{CompiledBinding ShowCategoryFilter}">
<Border Classes="card" VerticalAlignment="Stretch">
<StackPanel>
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}">Categories</TextBlock>
<Border Classes="card-separator" />
<ContentControl Content="{CompiledBinding CategoriesViewModel}"></ContentControl>
<CheckBox IsChecked="{CompiledBinding IncludeDefaultEntries}">
<StackPanel Orientation="Horizontal" Spacing="2">
<Image Source="/Assets/Images/Logo/bow.png" RenderOptions.BitmapInterpolationMode="HighQuality" Width="18" Height="18"/>
<TextBlock>Default entries</TextBlock>
</StackPanel>
</CheckBox>
</StackPanel>
</Border>
<Border Classes="card" VerticalAlignment="Stretch">
<StackPanel>
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}">Categories</TextBlock>
@@ -59,7 +74,7 @@
<TextBlock>
<Run>Modify or clear your filters to view other entries</Run>
</TextBlock>
<Lottie Path="/Assets/Animations/empty.json" RepeatCount="1" Width="350" Height="350"></Lottie>
<!-- <Lottie Path="/Assets/Animations/empty.json" RepeatCount="1" Width="350" Height="350"></Lottie> -->
</StackPanel>
</Panel>
</Grid>
@@ -30,6 +30,7 @@ public partial class EntryListViewModel : RoutableScreen
[Notify] private bool _initializing = true;
[Notify] private bool _fetchingMore;
[Notify] private int _entriesPerFetch;
[Notify] private bool _includeDefaultEntries;
[Notify] private Vector _scrollOffset;
protected EntryListViewModel(IWorkshopClient workshopClient,
@@ -51,13 +52,14 @@ public partial class EntryListViewModel : RoutableScreen
Entries = entries;
// Respond to filter query input changes
this.WhenAnyValue(vm => vm.IncludeDefaultEntries).Skip(1).Throttle(TimeSpan.FromMilliseconds(200)).Subscribe(_ => Reset());
this.WhenActivated(d =>
{
InputViewModel.WhenAnyValue(vm => vm.Search).Skip(1).Throttle(TimeSpan.FromMilliseconds(200)).Subscribe(_ => Reset()).DisposeWith(d);
InputViewModel.WhenAnyValue(vm => vm.SortBy).Skip(1).Throttle(TimeSpan.FromMilliseconds(200)).Subscribe(_ => Reset()).DisposeWith(d);
CategoriesViewModel.WhenAnyValue(vm => vm.CategoryFilters).Skip(1).Subscribe(_ => Reset()).DisposeWith(d);
});
// Load entries when the view model is first activated
this.WhenActivatedAsync(async _ =>
{
@@ -76,7 +78,7 @@ public partial class EntryListViewModel : RoutableScreen
public EntryType? EntryType { get; set; }
public ReadOnlyObservableCollection<EntryListItemViewModel> Entries { get; }
public async Task FetchMore(CancellationToken cancellationToken)
{
if (FetchingMore || _currentPageInfo != null && !_currentPageInfo.HasNextPage)
@@ -122,6 +124,7 @@ public partial class EntryListViewModel : RoutableScreen
And =
[
new EntryFilterInput {EntryType = new EntryTypeOperationFilterInput {Eq = EntryType}},
new EntryFilterInput {DefaultEntryInfo = IncludeDefaultEntries ? new DefaultEntryInfoFilterInput {EntryId = new LongOperationFilterInput {Ngt = 0}} : null},
..CategoriesViewModel.CategoryFilters ?? []
]
};
@@ -32,6 +32,7 @@
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBox Classes="search-box" Text="{CompiledBinding SearchEntryInput}" Watermark="Search submissions" Margin="0 0 10 0" />
<Button Grid.Column="1" HorizontalAlignment="Right" Classes="accent" Command="{CompiledBinding AddSubmission}">Add submission</Button>
</Grid>
<StackPanel Grid.Row="1" Grid.Column="0" IsVisible="{CompiledBinding Empty}" Margin="0 50 0 0" Classes="empty-state">
@@ -80,7 +80,7 @@ public partial class SpecificationsStepViewModel : SubmissionViewModel
if (State.Icon != null)
{
State.Icon.Seek(0, SeekOrigin.Begin);
viewModel.IconBitmap = BitmapExtensions.LoadAndResize(State.Icon, 128);
viewModel.IconBitmap = BitmapExtensions.LoadAndResize(State.Icon, 128, false);
}
EntrySpecificationsViewModel = viewModel;