Add AXAML hot reload (debug only)

Major progress on default entries too I guess lmao
This commit is contained in:
Robert committed 2025-11-15 15:07:54 +01:00
1 parent 20fb6b7662
commit b351f685f7
24 files changed
+963 -682

No files matched your search

+1 -1
View File
@@ -22,7 +22,7 @@
Foreground="White"
FontSize="32"
Margin="30"
Text=" Welcome to Artemis, the unified RGB platform.">
Text="Welcome to Artemis, the unified RGB platform.">
<TextBlock.Effect>
<DropShadowEffect Color="Black" OffsetX="2" OffsetY="2" BlurRadius="5"></DropShadowEffect>
</TextBlock.Effect>
@@ -7,26 +7,52 @@
x:Class="Artemis.UI.Screens.StartupWizard.Steps.DefaultEntriesStepView"
x:DataType="steps:DefaultEntriesStepViewModel">
<Border Classes="card">
<Grid RowDefinitions="Auto,Auto,Auto,Auto,*">
<StackPanel Grid.Row="0">
<Grid RowDefinitions="*,Auto">
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel>
<TextBlock TextWrapping="Wrap">
Installing default plugins and profiles, these will provide you with a basic setup to get started with Artemis.
Below is a list of default features that can be installed to get you started with Artemis. You can always install or uninstall features later via the Workshop.
</TextBlock>
</StackPanel>
<TextBlock Grid.Row="2">
<Run Text="Installed"/>
<Run Text="{CompiledBinding InstalledEntries}"/>
<Run Text=" of "/>
<Run Text="{CompiledBinding TotalEntries}"/>
<Run Text=" entries."/>
</TextBlock>
<TextBlock Classes="card-title" IsVisible="{CompiledBinding DeviceProviderEntryViewModels.Count}">
Device providers
</TextBlock>
<ItemsControl ItemsSource="{CompiledBinding DeviceProviderEntryViewModels}" Margin="0,0,5,0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="2" ColumnSpacing="5" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<ProgressBar Grid.Row="3" Minimum="0" Maximum="{CompiledBinding TotalEntries}" IsIndeterminate="{CompiledBinding FetchingDefaultEntries}" Value="{CompiledBinding InstalledEntries}" />
<StackPanel Grid.Row="4" Orientation="Vertical" IsVisible="{CompiledBinding CurrentEntry, Converter={x:Static ObjectConverters.IsNotNull}}" Margin="0 10">
<TextBlock Text="{CompiledBinding CurrentEntry.Name, StringFormat='Currently installing: {0}'}" />
<ProgressBar Minimum="0" Maximum="100" Value="{CompiledBinding InstallProgress}" />
<TextBlock Classes="card-title" IsVisible="{CompiledBinding EssentialEntryViewModels.Count}">
Essentials
</TextBlock>
<ItemsControl ItemsSource="{CompiledBinding EssentialEntryViewModels}" Margin="0,0,5,0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="2" ColumnSpacing="5"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<TextBlock Classes="card-title" IsVisible="{CompiledBinding OtherEntryViewModels.Count}">
Other features
</TextBlock>
<ItemsControl ItemsSource="{CompiledBinding OtherEntryViewModels}" Margin="0,0,5,0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="2" ColumnSpacing="5"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</StackPanel>
</Grid>
</Border>
</ScrollViewer>
<ProgressBar Grid.Row="1"
Minimum="0"
Maximum="{CompiledBinding TotalEntries}"
IsIndeterminate="{CompiledBinding FetchingDefaultEntries}"
Value="{CompiledBinding CurrentEntries}" />
</Grid>
</Border>
</UserControl>
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -8,7 +9,6 @@ using Artemis.UI.Extensions;
using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Utilities;
using Artemis.WebClient.Workshop;
using Artemis.WebClient.Workshop.Handlers.InstallationHandlers;
using Artemis.WebClient.Workshop.Services;
using PropertyChanged.SourceGenerator;
using ReactiveUI;
@@ -20,25 +20,31 @@ public partial class DefaultEntriesStepViewModel : WizardStepViewModel
{
[Notify] private bool _workshopReachable;
[Notify] private bool _fetchingDefaultEntries;
[Notify] private int _totalEntries;
[Notify] private int _installedEntries;
[Notify] private int _installProgress;
[Notify] private IEntrySummary? _currentEntry;
[Notify] private bool _installed;
[Notify] private int _currentEntries;
[Notify] private int _totalEntries = 1;
private readonly IWorkshopService _workshopService;
private readonly IWorkshopClient _client;
private readonly IWindowService _windowService;
private readonly Progress<StreamProgress> _currentEntryProgress = new();
private readonly Func<IEntrySummary, DefaultEntryItemViewModel> _getDefaultEntryItemViewModel;
public ObservableCollection<DefaultEntryItemViewModel> DeviceProviderEntryViewModels { get; } = [];
public ObservableCollection<DefaultEntryItemViewModel> EssentialEntryViewModels { get; } = [];
public ObservableCollection<DefaultEntryItemViewModel> OtherEntryViewModels { get; } = [];
public DefaultEntriesStepViewModel(IWorkshopService workshopService, IDeviceService deviceService, IWorkshopClient client, IWindowService windowService)
public DefaultEntriesStepViewModel(IWorkshopService workshopService, IDeviceService deviceService, IWorkshopClient client,
Func<IEntrySummary, DefaultEntryItemViewModel> getDefaultEntryItemViewModel)
{
_workshopService = workshopService;
_client = client;
_windowService = windowService;
_currentEntryProgress.ProgressChanged += (_, f) => InstallProgress = f.ProgressPercentage;
_getDefaultEntryItemViewModel = getDefaultEntryItemViewModel;
Continue = ReactiveCommand.Create(() => Wizard.ChangeScreen<SettingsStepViewModel>());
ContinueText = "Install selected entries";
Continue = ReactiveCommand.CreateFromTask(async ct =>
{
if (Installed)
Wizard.ChangeScreen<SettingsStepViewModel>();
else
await Install(ct);
});
GoBack = ReactiveCommand.Create(() =>
{
if (deviceService.EnabledDevices.Count == 0)
@@ -49,22 +55,57 @@ public partial class DefaultEntriesStepViewModel : WizardStepViewModel
this.WhenActivatedAsync(async d =>
{
WorkshopReachable = await _workshopService.ValidateWorkshopStatus(d.AsCancellationToken());
WorkshopReachable = await workshopService.ValidateWorkshopStatus(d.AsCancellationToken());
if (WorkshopReachable)
{
await InstallDefaultEntries(d.AsCancellationToken());
await GetDefaultEntries(d.AsCancellationToken());
}
});
}
public async Task<bool> InstallDefaultEntries(CancellationToken cancellationToken)
private async Task Install(CancellationToken cancellationToken)
{
FetchingDefaultEntries = true;
TotalEntries = 0;
InstalledEntries = 0;
// Remove entries that aren't to be installed
RemoveUnselectedEntries(DeviceProviderEntryViewModels);
RemoveUnselectedEntries(EssentialEntryViewModels);
RemoveUnselectedEntries(OtherEntryViewModels);
TotalEntries = DeviceProviderEntryViewModels.Count + EssentialEntryViewModels.Count + OtherEntryViewModels.Count;
CurrentEntries = 0;
// Install entries one by one, removing them from the list as we go
List<DefaultEntryItemViewModel> entries = [..DeviceProviderEntryViewModels, ..EssentialEntryViewModels, ..OtherEntryViewModels];
foreach (DefaultEntryItemViewModel defaultEntryItemViewModel in entries)
{
cancellationToken.ThrowIfCancellationRequested();
bool removeFromList = await defaultEntryItemViewModel.InstallEntry(cancellationToken);
if (!removeFromList)
break;
DeviceProviderEntryViewModels.Remove(defaultEntryItemViewModel);
EssentialEntryViewModels.Remove(defaultEntryItemViewModel);
OtherEntryViewModels.Remove(defaultEntryItemViewModel);
CurrentEntries++;
}
Installed = true;
ContinueText = "Continue";
}
private void RemoveUnselectedEntries(ObservableCollection<DefaultEntryItemViewModel> entryViewModels)
{
List<DefaultEntryItemViewModel> toRemove = entryViewModels.Where(e => !e.ShouldInstall).ToList();
foreach (DefaultEntryItemViewModel defaultEntryItemViewModel in toRemove)
entryViewModels.Remove(defaultEntryItemViewModel);
}
private async Task GetDefaultEntries(CancellationToken cancellationToken)
{
if (!WorkshopReachable)
return false;
return;
FetchingDefaultEntries = true;
IOperationResult<IGetDefaultEntriesResult> result = await _client.GetDefaultEntries.ExecuteAsync(100, null, cancellationToken);
List<IEntrySummary> entries = result.Data?.EntriesV2?.Edges?.Select(e => e.Node).Cast<IEntrySummary>().ToList() ?? [];
@@ -75,41 +116,25 @@ public partial class DefaultEntriesStepViewModel : WizardStepViewModel
entries.AddRange(result.Data.EntriesV2.Edges.Select(e => e.Node));
}
await Task.Delay(1000);
FetchingDefaultEntries = false;
TotalEntries = entries.Count;
if (entries.Count == 0)
return false;
DeviceProviderEntryViewModels.Clear();
EssentialEntryViewModels.Clear();
OtherEntryViewModels.Clear();
foreach (IEntrySummary entry in entries)
{
if (cancellationToken.IsCancellationRequested)
return false;
CurrentEntry = entry;
// Skip entries without a release and entries that are already installed
if (entry.LatestRelease == null || _workshopService.GetInstalledEntry(entry.Id) != null)
{
InstalledEntries++;
if (entry.DefaultEntryInfo == null)
continue;
}
EntryInstallResult installResult = await _workshopService.InstallEntry(entry, entry.LatestRelease, _currentEntryProgress, cancellationToken);
// Unlikely as default entries do nothing fancy
if (!installResult.IsSuccess)
{
await _windowService.CreateContentDialog().WithTitle("Failed to install entry")
.WithContent($"Failed to install entry '{entry.Name}' ({entry.Id}): {installResult.Message}")
.WithCloseButtonText("Skip and continue")
.ShowAsync();
}
InstalledEntries++;
DefaultEntryItemViewModel viewModel = _getDefaultEntryItemViewModel(entry);
viewModel.ShouldInstall = entry.DefaultEntryInfo.IsEssential;
if (entry.DefaultEntryInfo.IsDeviceProvider)
DeviceProviderEntryViewModels.Add(viewModel);
else if (entry.DefaultEntryInfo.IsEssential)
EssentialEntryViewModels.Add(viewModel);
else
OtherEntryViewModels.Add(viewModel);
}
return true;
FetchingDefaultEntries = false;
}
}
@@ -0,0 +1,72 @@
<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:il="clr-namespace:AsyncImageLoader;assembly=AsyncImageLoader.Avalonia"
xmlns:converters="clr-namespace:Artemis.UI.Converters"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
xmlns:steps="clr-namespace:Artemis.UI.Screens.StartupWizard.Steps"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.StartupWizard.Steps.DefaultEntryItemView"
x:DataType="steps:DefaultEntryItemViewModel">
<UserControl.Resources>
<converters:EntryIconUriConverter x:Key="EntryIconUriConverter" />
<converters:DateTimeConverter x:Key="DateTimeConverter" />
</UserControl.Resources>
<Border Classes="card" Padding="15" Margin="0 5">
<Grid ColumnDefinitions="Auto,*,Auto,Auto" RowDefinitions="*, Auto">
<!-- Icon -->
<Border Grid.Column="0"
Grid.Row="0"
Grid.RowSpan="2"
CornerRadius="6"
VerticalAlignment="Center"
Margin="0 0 10 0"
Width="80"
Height="80"
ClipToBounds="True">
<Image Stretch="UniformToFill" il:ImageLoader.Source="{CompiledBinding Entry.Id, Converter={StaticResource EntryIconUriConverter}, Mode=OneWay}" />
</Border>
<!-- Body -->
<Grid Grid.Column="1" Grid.Row="0" Grid.RowSpan="2" VerticalAlignment="Stretch" RowDefinitions="Auto,*,Auto">
<StackPanel Grid.Row="0" Orientation="Horizontal">
<TextBlock Classes="h5" Margin="0 0 0 5" TextTrimming="CharacterEllipsis" Text="{CompiledBinding Entry.Name, FallbackValue=Title}" />
</StackPanel>
<TextBlock Grid.Row="1"
Classes="subtitle"
TextWrapping="Wrap"
TextTrimming="CharacterEllipsis"
Text="{CompiledBinding Entry.Summary, FallbackValue=Summary}" />
<ItemsControl Grid.Row="2" ItemsSource="{CompiledBinding Entry.Categories}" Margin="0 8 0 0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Spacing="8" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<avalonia:MaterialIcon Kind="{CompiledBinding Icon}" Margin="0 0 3 0" />
<TextBlock Text="{CompiledBinding Name}" TextTrimming="CharacterEllipsis" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
<!-- Management -->
<Border Grid.Column="3" Grid.Row="0" Grid.RowSpan="2" BorderBrush="{DynamicResource ButtonBorderBrush}" BorderThickness="1 0 0 0" Margin="10 0 0 0" Padding="10 0 0 0" IsVisible="{CompiledBinding !IsInstalled}">
<StackPanel VerticalAlignment="Center">
<CheckBox MinHeight="26" Margin="0 4 0 0" IsChecked="{CompiledBinding ShouldInstall}">Install</CheckBox>
</StackPanel>
</Border>
<Border Grid.Column="3" Grid.Row="0" Grid.RowSpan="2" BorderBrush="{DynamicResource ButtonBorderBrush}" BorderThickness="1 0 0 0" Margin="10 0 0 0" Padding="10 0 0 0" IsVisible="{CompiledBinding IsInstalled}">
<StackPanel VerticalAlignment="Center">
<TextBlock>Already installed</TextBlock>
</StackPanel>
</Border>
</Grid>
</Border>
</UserControl>
@@ -0,0 +1,11 @@
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.StartupWizard.Steps;
public partial class DefaultEntryItemView : ReactiveUserControl<StartupWizard.Steps.DefaultEntryItemViewModel>
{
public DefaultEntryItemView()
{
InitializeComponent();
}
}
@@ -0,0 +1,86 @@
using System;
using System.Linq;
using System.Reactive.Disposables;
using System.Threading;
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.DryIoc.Factories;
using Artemis.UI.Screens.Plugins;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Utilities;
using Artemis.WebClient.Workshop;
using Artemis.WebClient.Workshop.Handlers.InstallationHandlers;
using Artemis.WebClient.Workshop.Models;
using Artemis.WebClient.Workshop.Services;
using PropertyChanged.SourceGenerator;
using ReactiveUI;
using StrawberryShake;
namespace Artemis.UI.Screens.StartupWizard.Steps;
public partial class DefaultEntryItemViewModel : ActivatableViewModelBase
{
private readonly IWorkshopService _workshopService;
private readonly IWindowService _windowService;
private readonly IPluginManagementService _pluginManagementService;
private readonly ISettingsVmFactory _settingsVmFactory;
private readonly Progress<StreamProgress> _progress = new();
[Notify] private bool _isInstalled;
[Notify] private bool _shouldInstall;
public DefaultEntryItemViewModel(IEntrySummary entry, IWorkshopService workshopService, IWindowService windowService, IPluginManagementService pluginManagementService, ISettingsVmFactory settingsVmFactory)
{
_workshopService = workshopService;
_windowService = windowService;
_pluginManagementService = pluginManagementService;
_settingsVmFactory = settingsVmFactory;
Entry = entry;
this.WhenActivated((CompositeDisposable _) => { IsInstalled = workshopService.GetInstalledEntry(entry.Id) != null; });
}
public IEntrySummary Entry { get; }
public async Task<bool> InstallEntry(CancellationToken cancellationToken)
{
if (IsInstalled || !ShouldInstall || Entry.LatestRelease == null)
return true;
// Most entries install so quick it looks broken without a small delay
Task minimumDelay = Task.Delay(100, cancellationToken);
EntryInstallResult result = await _workshopService.InstallEntry(Entry, Entry.LatestRelease, _progress, cancellationToken);
await minimumDelay;
if (!result.IsSuccess)
{
await _windowService.CreateContentDialog().WithTitle("Failed to install entry")
.WithContent($"Failed to install entry '{Entry.Name}' ({Entry.Id}): {result.Message}")
.WithCloseButtonText("Skip and continue")
.ShowAsync();
}
// If the entry is a plugin, enable the plugin
else if (result.Entry?.EntryType == EntryType.Plugin)
{
await EnablePlugin(result.Entry);
}
return result.IsSuccess;
}
private async Task EnablePlugin(InstalledEntry entry)
{
if (!entry.TryGetMetadata("PluginId", out Guid pluginId))
throw new InvalidOperationException("Plugin entry does not contain a PluginId metadata value.");
Plugin? plugin = _pluginManagementService.GetAllPlugins().FirstOrDefault(p => p.Guid == pluginId);
if (plugin == null)
throw new InvalidOperationException($"Plugin with id '{pluginId}' does not exist.");
// There's quite a bit of UI involved in enabling a plugin, borrowing the PluginSettingsViewModel for this
PluginViewModel pluginViewModel = _settingsVmFactory.PluginViewModel(plugin, ReactiveCommand.Create(() => { }));
await pluginViewModel.UpdateEnabled(true);
}
}
@@ -63,8 +63,12 @@
<Label Target="Summary" Margin="0 5 0 0">Summary</Label>
<TextBox Name="Summary" Text="{CompiledBinding Summary}"></TextBox>
<CheckBox IsVisible="{CompiledBinding IsAdministrator}" IsChecked="{CompiledBinding IsDefault}">Download by default (admin only)</CheckBox>
<StackPanel IsVisible="{CompiledBinding IsAdministrator}" Orientation="Horizontal">
<CheckBox IsChecked="{CompiledBinding IsDefault}">Download by default (admin only)</CheckBox>
<CheckBox IsEnabled="{CompiledBinding IsDefault}" IsChecked="{CompiledBinding IsEssential}">Essential</CheckBox>
<CheckBox IsEnabled="{CompiledBinding IsDefault}" IsChecked="{CompiledBinding IsDeviceProvider}">Device provider</CheckBox>
</StackPanel>
</StackPanel>
</Grid>
@@ -93,10 +97,10 @@
<Label>Tags</Label>
<tagsInput:TagsInput Tags="{CompiledBinding Tags}" />
</StackPanel>
<controls:SplitMarkdownEditor Grid.Row="1" Title="Description" Markdown="{CompiledBinding Description}"/>
<TextBlock Grid.Row="2"
<controls:SplitMarkdownEditor Grid.Row="1" Title="Description" Markdown="{CompiledBinding Description}" />
<TextBlock Grid.Row="2"
Foreground="{DynamicResource SystemFillColorCriticalBrush}"
Margin="2 8 0 0"
IsVisible="{CompiledBinding !DescriptionValid}">
@@ -36,6 +36,8 @@ public partial class EntrySpecificationsViewModel : ValidatableViewModelBase
[Notify] private string _summary = string.Empty;
[Notify] private string _description = string.Empty;
[Notify] private bool _isDefault;
[Notify] private bool _isEssential;
[Notify] private bool _isDeviceProvider;
[Notify] private Bitmap? _iconBitmap;
[Notify(Setter.Private)] private bool _iconChanged;
@@ -105,7 +105,9 @@ public partial class SubmissionDetailsViewModel : RoutableScreen
specificationsViewModel.Name = Entry.Name;
specificationsViewModel.Summary = Entry.Summary;
specificationsViewModel.Description = Entry.Description;
specificationsViewModel.IsDefault = Entry.IsDefault;
specificationsViewModel.IsDefault = Entry.DefaultEntryInfo != null;
specificationsViewModel.IsEssential = Entry.DefaultEntryInfo?.IsEssential ?? false;
specificationsViewModel.IsDeviceProvider = Entry.DefaultEntryInfo?.IsDeviceProvider ?? false;
specificationsViewModel.PreselectedCategories = Entry.Categories.Select(c => c.Id).ToList();
specificationsViewModel.Tags.Clear();
@@ -170,14 +172,29 @@ public partial class SubmissionDetailsViewModel : RoutableScreen
HasChanges = EntrySpecificationsViewModel.Name != Entry.Name ||
EntrySpecificationsViewModel.Description != Entry.Description ||
EntrySpecificationsViewModel.Summary != Entry.Summary ||
EntrySpecificationsViewModel.IsDefault != Entry.IsDefault ||
EntrySpecificationsViewModel.IconChanged ||
HasAdminChanges() ||
!tags.SequenceEqual(Entry.Tags.Select(t => t.Name).OrderBy(t => t)) ||
!categories.SequenceEqual(Entry.Categories.Select(c => c.Id).OrderBy(c => c)) ||
Images.Any(i => i.HasChanges) ||
_removedImages.Any();
}
private bool HasAdminChanges()
{
if (EntrySpecificationsViewModel == null || Entry == null)
return false;
bool isDefault = Entry.DefaultEntryInfo != null;
bool isEssential = Entry.DefaultEntryInfo?.IsEssential ?? false;
bool isDeviceProvider = Entry.DefaultEntryInfo?.IsDeviceProvider ?? false;
return EntrySpecificationsViewModel.IsDefault != isDefault ||
(EntrySpecificationsViewModel.IsDefault && (
EntrySpecificationsViewModel.IsEssential != isEssential ||
EntrySpecificationsViewModel.IsDeviceProvider != isDeviceProvider));
}
private async Task ExecuteDiscardChanges()
{
await ApplyDetailsFromEntry(CancellationToken.None);
@@ -194,9 +211,15 @@ public partial class SubmissionDetailsViewModel : RoutableScreen
Name = EntrySpecificationsViewModel.Name,
Summary = EntrySpecificationsViewModel.Summary,
Description = EntrySpecificationsViewModel.Description,
IsDefault = EntrySpecificationsViewModel.IsDefault,
Categories = EntrySpecificationsViewModel.SelectedCategories,
Tags = EntrySpecificationsViewModel.Tags
Tags = EntrySpecificationsViewModel.Tags,
DefaultEntryInfo = EntrySpecificationsViewModel.IsDefault
? new DefaultEntryInfoInput
{
IsEssential = EntrySpecificationsViewModel.IsEssential,
IsDeviceProvider = EntrySpecificationsViewModel.IsDeviceProvider
}
: null
};
IOperationResult<IUpdateEntryResult> result = await _client.UpdateEntry.ExecuteAsync(input, cancellationToken);
@@ -33,6 +33,8 @@ public class SubmissionWizardState : IDisposable
public string Summary { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public bool IsDefault { get; set; }
public bool IsEssential { get; set; }
public bool IsDeviceProvider { get; set; }
public List<long> Categories { get; set; } = new();
public List<string> Tags { get; set; } = new();
@@ -66,6 +66,8 @@ public partial class SpecificationsStepViewModel : SubmissionViewModel
viewModel.Summary = State.Summary;
viewModel.Description = State.Description;
viewModel.IsDefault = State.IsDefault;
viewModel.IsEssential = State.IsEssential;
viewModel.IsDeviceProvider = State.IsDeviceProvider;
// Tags
viewModel.Tags.Clear();
@@ -95,6 +97,8 @@ public partial class SpecificationsStepViewModel : SubmissionViewModel
State.Summary = EntrySpecificationsViewModel.Summary;
State.Description = EntrySpecificationsViewModel.Description;
State.IsDefault = EntrySpecificationsViewModel.IsDefault;
State.IsEssential = EntrySpecificationsViewModel.IsEssential;
State.IsDeviceProvider = EntrySpecificationsViewModel.IsDeviceProvider;
// Categories and tasks
State.Categories = EntrySpecificationsViewModel.Categories.Where(c => c.IsSelected).Select(c => c.Id).ToList();
@@ -105,7 +105,14 @@ public partial class UploadStepViewModel : SubmissionViewModel
Summary = State.Summary,
Description = State.Description,
Categories = State.Categories,
Tags = State.Tags
Tags = State.Tags,
DefaultEntryInfo = State.IsDefault
? new DefaultEntryInfoInput
{
IsEssential = State.IsEssential,
IsDeviceProvider = State.IsDeviceProvider
}
: null
}, cancellationToken);
result.EnsureNoErrors();