1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2026-02-04 02:43:32 +00:00

Workshop - Improved workshop entry compatibility handling

Workshop - Added minimum Artemis version to releases
This commit is contained in:
Robert 2025-12-22 20:13:49 +01:00
parent 86f2426f37
commit 6bf5a11108
21 changed files with 174 additions and 45 deletions

View File

@ -113,7 +113,8 @@ public class PluginInfo : IPrerequisitesSubject
/// <summary> /// <summary>
/// Gets the minimum version of Artemis required by this plugin /// Gets the minimum version of Artemis required by this plugin
/// </summary> /// </summary>
public Version? MinimumVersion { get; internal init; } = new(1, 0, 0); [JsonInclude]
public Version? MinimumVersion { get; internal init; }
/// <summary> /// <summary>
/// Gets the plugin this info is associated with /// Gets the plugin this info is associated with

View File

@ -29,7 +29,8 @@ public interface IWindowService : IArtemisSharedUIService
/// </summary> /// </summary>
/// <param name="title">The title of the dialog</param> /// <param name="title">The title of the dialog</param>
/// <param name="exception">The exception to display</param> /// <param name="exception">The exception to display</param>
void ShowExceptionDialog(string title, Exception exception); /// <param name="customMessage"></param>
void ShowExceptionDialog(string title, Exception exception, string? customMessage = null);
/// <summary> /// <summary>
/// Creates a view model instance of type <typeparamref name="TViewModel" /> and shows its corresponding View as a /// Creates a view model instance of type <typeparamref name="TViewModel" /> and shows its corresponding View as a

View File

@ -22,9 +22,7 @@
<StackPanel Grid.Row="2" Margin="20"> <StackPanel Grid.Row="2" Margin="20">
<TextBlock Classes="h3">Awww :(</TextBlock> <TextBlock Classes="h3">Awww :(</TextBlock>
<TextBlock> <TextBlock Text="{CompiledBinding Message}"/>
It looks like Artemis ran into an unexpected error. If this keeps happening feel free to hit us up on Discord.
</TextBlock>
</StackPanel> </StackPanel>
<Grid Grid.Row="3" ColumnDefinitions="*,Auto" RowDefinitions="*,Auto"> <Grid Grid.Row="3" ColumnDefinitions="*,Auto" RowDefinitions="*,Auto">

View File

@ -9,15 +9,17 @@ internal class ExceptionDialogViewModel : DialogViewModelBase<object>
{ {
private readonly INotificationService _notificationService; private readonly INotificationService _notificationService;
public ExceptionDialogViewModel(string title, Exception exception, INotificationService notificationService) public ExceptionDialogViewModel(string title, Exception exception, string? customMessage, INotificationService notificationService)
{ {
_notificationService = notificationService; _notificationService = notificationService;
Title = $"Artemis | {title}"; Title = $"Artemis | {title}";
Message = customMessage ?? "It looks like Artemis ran into an unexpected error. If this keeps happening feel free to hit us up on Discord.";
Exception = exception; Exception = exception;
} }
public string Title { get; } public string Title { get; }
public string Message { get; }
public Exception Exception { get; } public Exception Exception { get; }
public async Task CopyException() public async Task CopyException()

View File

@ -125,7 +125,7 @@ internal class WindowService : IWindowService
return await window.ShowDialog<TResult>(parent); return await window.ShowDialog<TResult>(parent);
} }
public void ShowExceptionDialog(string title, Exception exception) public void ShowExceptionDialog(string title, Exception exception, string? customMessage = null)
{ {
if (_exceptionDialogOpen) if (_exceptionDialogOpen)
return; return;
@ -136,7 +136,7 @@ internal class WindowService : IWindowService
{ {
try try
{ {
await ShowDialogAsync(new ExceptionDialogViewModel(title, exception, _container.Resolve<INotificationService>())); await ShowDialogAsync(new ExceptionDialogViewModel(title, exception, customMessage, _container.Resolve<INotificationService>()));
} }
finally finally
{ {

View File

@ -0,0 +1,23 @@
using System;
using Artemis.Core;
using Artemis.WebClient.Workshop;
namespace Artemis.UI.Extensions;
public static class ReleaseExtensions
{
extension(IRelease release)
{
/// <summary>
/// Determines whether the release is compatible with the current version of Artemis.
/// </summary>
/// <returns>A value indicating whether the release is compatible with the current version of Artemis.</returns>
public bool IsCompatible()
{
if (release.MinimumVersion == null || Constants.CurrentVersion == "local")
return true;
return release.MinimumVersion <= Version.Parse(Constants.CurrentVersion).ArtemisVersionToLong();
}
}
}

View File

@ -0,0 +1,45 @@
using System;
namespace Artemis.UI.Extensions;
public static class VersionExtensions
{
/// <param name="version">The version to convert</param>
extension(Version version)
{
/// <summary>
/// Convert a Version to a long representation for easy comparison in PostgreSQL
/// <remarks>Assumes format: major.year.dayOfYear.revision (e.g., 1.2024.0225.2)</remarks>
/// </summary>
/// <returns>A long value that preserves version comparison order</returns>
public long ArtemisVersionToLong()
{
// Format: major.year.dayOfYear.revision
// Convert to: majorYYYYDDDRRRR (16 digits)
// Major: 1 digit (0-9)
// Year: 4 digits (e.g., 2024)
// Day: 3 digits (001-366, padded)
// Revision: 4 digits (0000-9999, padded)
long major = Math.Max(0, Math.Min(9, version.Major));
long year = Math.Max(1000, Math.Min(9999, version.Minor));
long day = Math.Max(1, Math.Min(366, version.Build));
long revision = Math.Max(0, Math.Min(9999, version.Revision >= 0 ? version.Revision : 0));
return major * 100000000000L +
year * 10000000L +
day * 10000L +
revision;
}
public static Version FromLong(long versionLong)
{
int major = (int)(versionLong / 100000000000L);
int year = (int)((versionLong / 10000000L) % 10000);
int day = (int)((versionLong / 10000L) % 1000);
int revision = (int)(versionLong % 10000L);
return new Version(major, year, day, revision);
}
}
}

View File

@ -90,11 +90,11 @@
<!-- Install state --> <!-- Install state -->
<StackPanel Grid.Column="2" Grid.Row="1" Margin="0 0 4 0" HorizontalAlignment="Right" VerticalAlignment="Bottom" IsVisible="{CompiledBinding IsInstalled}"> <StackPanel Grid.Column="2" Grid.Row="1" Margin="0 0 4 0" HorizontalAlignment="Right" VerticalAlignment="Bottom" IsVisible="{CompiledBinding IsInstalled}">
<TextBlock TextAlignment="Right" IsVisible="{CompiledBinding !UpdateAvailable}"> <TextBlock TextAlignment="Right" IsVisible="{CompiledBinding !UpdateAvailable}">
<avalonia:MaterialIcon Kind="CheckCircle" Foreground="{DynamicResource SystemAccentColorLight1}" Width="20" Height="20" /> <avalonia:MaterialIcon Kind="CheckCircle" Foreground="{DynamicResource SystemAccentColorLight1}" Width="20" Height="20" Margin="0 0 0 -4"/>
<Run>installed</Run> <Run>installed</Run>
</TextBlock> </TextBlock>
<TextBlock TextAlignment="Right" IsVisible="{CompiledBinding UpdateAvailable}"> <TextBlock TextAlignment="Right" IsVisible="{CompiledBinding UpdateAvailable}">
<avalonia:MaterialIcon Kind="Update" Foreground="{DynamicResource SystemAccentColorLight1}" Width="20" Height="20" /> <avalonia:MaterialIcon Kind="Update" Foreground="{DynamicResource SystemAccentColorLight1}" Width="20" Height="20" Margin="0 0 0 -4"/>
<Run>update available</Run> <Run>update available</Run>
</TextBlock> </TextBlock>
</StackPanel> </StackPanel>

View File

@ -30,7 +30,7 @@ public partial class EntryListViewModel : RoutableScreen
[Notify] private bool _initializing = true; [Notify] private bool _initializing = true;
[Notify] private bool _fetchingMore; [Notify] private bool _fetchingMore;
[Notify] private int _entriesPerFetch; [Notify] private int _entriesPerFetch;
[Notify] private bool _includeDefaultEntries; [Notify] private bool _includeDefaultEntries = true;
[Notify] private Vector _scrollOffset; [Notify] private Vector _scrollOffset;
protected EntryListViewModel(EntryType entryType, protected EntryListViewModel(EntryType entryType,

View File

@ -37,13 +37,17 @@
</Style> </Style>
</UserControl.Styles> </UserControl.Styles>
<StackPanel> <StackPanel>
<Grid ColumnDefinitions="Auto,*,Auto"> <Grid ColumnDefinitions="Auto,*,Auto,Auto">
<Button Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Classes="icon-button" Margin="0 0 5 0" Command="{CompiledBinding Close}" IsVisible="{CompiledBinding InDetailsScreen}"> <Button Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Classes="icon-button" Margin="0 0 5 0" Command="{CompiledBinding Close}" IsVisible="{CompiledBinding InDetailsScreen}">
<avalonia:MaterialIcon Kind="ArrowBack" /> <avalonia:MaterialIcon Kind="ArrowBack" />
</Button> </Button>
<TextBlock Grid.Row="0" Grid.Column="1" Theme="{StaticResource SubtitleTextBlockStyle}" IsVisible="{CompiledBinding InDetailsScreen}">Release info</TextBlock> <TextBlock Grid.Row="0" Grid.Column="1" Theme="{StaticResource SubtitleTextBlockStyle}" IsVisible="{CompiledBinding InDetailsScreen}">Release info</TextBlock>
<TextBlock Grid.Row="0" Grid.Column="1" Theme="{StaticResource SubtitleTextBlockStyle}" IsVisible="{CompiledBinding !InDetailsScreen}">Latest release</TextBlock> <TextBlock Grid.Row="0" Grid.Column="1" Theme="{StaticResource SubtitleTextBlockStyle}" IsVisible="{CompiledBinding !InDetailsScreen}">Latest release</TextBlock>
<StackPanel Grid.Column="2"> <StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="5" Margin="0 0 15 0" IsVisible="{CompiledBinding IncompatibilityReason, Converter={x:Static ObjectConverters.IsNotNull}}">
<avalonia:MaterialIcon Kind="Alert" Width="20" Height="20" Foreground="#DAA520" />
<TextBlock VerticalAlignment="Center" Text="{CompiledBinding IncompatibilityReason}"/>
</StackPanel>
<StackPanel Grid.Column="3">
<!-- Install progress --> <!-- Install progress -->
<StackPanel Orientation="Horizontal" <StackPanel Orientation="Horizontal"
Spacing="5" Spacing="5"
@ -64,11 +68,15 @@
<!-- Install button --> <!-- Install button -->
<Panel IsVisible="{CompiledBinding !InstallationInProgress}" HorizontalAlignment="Right"> <Panel IsVisible="{CompiledBinding !InstallationInProgress}" HorizontalAlignment="Right">
<Button IsVisible="{CompiledBinding !IsCurrentVersion}" Classes="accent" Width="80" Command="{CompiledBinding Install}"> <Button IsVisible="{CompiledBinding !IsCurrentVersion}"
Classes="accent"
Width="80"
Command="{CompiledBinding Install}"
IsEnabled="{CompiledBinding IncompatibilityReason, Converter={x:Static ObjectConverters.IsNull}}">
Install Install
</Button> </Button>
<StackPanel IsVisible="{CompiledBinding IsCurrentVersion}" Orientation="Horizontal" Spacing="10"> <StackPanel IsVisible="{CompiledBinding IsCurrentVersion}" Orientation="Horizontal" Spacing="10">
<Button Classes="accent" Width="80" Command="{CompiledBinding Reinstall}"> <Button Classes="accent" Width="80" Command="{CompiledBinding Reinstall}">
Re-install Re-install
</Button> </Button>
<Button Width="80" Command="{CompiledBinding Uninstall}">Uninstall</Button> <Button Width="80" Command="{CompiledBinding Uninstall}">Uninstall</Button>

View File

@ -7,6 +7,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Extensions;
using Artemis.UI.Screens.Workshop.EntryReleases.Dialogs; using Artemis.UI.Screens.Workshop.EntryReleases.Dialogs;
using Artemis.UI.Services.Interfaces; using Artemis.UI.Services.Interfaces;
using Artemis.UI.Shared; using Artemis.UI.Shared;
@ -38,6 +39,7 @@ public partial class EntryReleaseInfoViewModel : ActivatableViewModelBase
[Notify] private bool _isCurrentVersion; [Notify] private bool _isCurrentVersion;
[Notify] private bool _installationInProgress; [Notify] private bool _installationInProgress;
[Notify] private bool _inDetailsScreen; [Notify] private bool _inDetailsScreen;
[Notify] private string? _incompatibilityReason;
private CancellationTokenSource? _cts; private CancellationTokenSource? _cts;
@ -67,9 +69,11 @@ public partial class EntryReleaseInfoViewModel : ActivatableViewModelBase
}).DisposeWith(d); }).DisposeWith(d);
IsCurrentVersion = Release != null && _workshopService.GetInstalledEntry(Release.Entry.Id)?.ReleaseId == Release.Id; IsCurrentVersion = Release != null && _workshopService.GetInstalledEntry(Release.Entry.Id)?.ReleaseId == Release.Id;
IncompatibilityReason = Release != null && !Release.IsCompatible() ? $"Requires Artemis v{Version.FromLong(Release.MinimumVersion!.Value)} or later" : null;
}); });
this.WhenAnyValue(vm => vm.Release).Subscribe(r => IsCurrentVersion = r != null && _workshopService.GetInstalledEntry(r.Entry.Id)?.ReleaseId == r.Id); this.WhenAnyValue(vm => vm.Release).Subscribe(r => IsCurrentVersion = r != null && _workshopService.GetInstalledEntry(r.Entry.Id)?.ReleaseId == r.Id);
this.WhenAnyValue(vm => vm.Release).Subscribe(r => IncompatibilityReason = r != null && !r.IsCompatible() ? $"Requires Artemis v{Version.FromLong(r.MinimumVersion!.Value)} or later" : null);
InDetailsScreen = true; InDetailsScreen = true;
} }
@ -131,7 +135,17 @@ public partial class EntryReleaseInfoViewModel : ActivatableViewModelBase
} }
else if (!_cts.IsCancellationRequested) else if (!_cts.IsCancellationRequested)
{ {
_notificationService.CreateNotification().WithTitle("Installation failed").WithMessage(result.Message).WithSeverity(NotificationSeverity.Error).Show(); if (result.Exception != null)
{
// Not taking the fall on this one :')
_windowService.ShowExceptionDialog(
"Failed to install workshop entry",
result.Exception,
"Make sure the entry is compatible with this version of Artemis or reach out to the author for support."
);
}
else
await _windowService.ShowConfirmContentDialog("Failed to install workshop entry", result.Message ?? "Unknown error", "Close", null);
} }
} }
catch (Exception e) catch (Exception e)

View File

@ -30,5 +30,6 @@
</TextBlock> </TextBlock>
</StackPanel> </StackPanel>
<avalonia:MaterialIcon Classes="status-icon" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Right" Kind="CheckCircle" ToolTip.Tip="Current version" IsVisible="{CompiledBinding IsCurrentVersion}" /> <avalonia:MaterialIcon Classes="status-icon" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Right" Kind="CheckCircle" ToolTip.Tip="Current version" IsVisible="{CompiledBinding IsCurrentVersion}" />
<avalonia:MaterialIcon Classes="status-icon" Foreground="#DAA520" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Right" Kind="Alert" ToolTip.Tip="{CompiledBinding IncompatibilityReason}" IsVisible="{CompiledBinding IncompatibilityReason, Converter={x:Static ObjectConverters.IsNotNull}}" />
</Grid> </Grid>
</UserControl> </UserControl>

View File

@ -1,5 +1,7 @@
using System.Reactive.Disposables; using System;
using System.Reactive.Disposables;
using System.Reactive.Disposables.Fluent; using System.Reactive.Disposables.Fluent;
using Artemis.UI.Extensions;
using Artemis.UI.Shared; using Artemis.UI.Shared;
using Artemis.WebClient.Workshop; using Artemis.WebClient.Workshop;
using Artemis.WebClient.Workshop.Models; using Artemis.WebClient.Workshop.Models;
@ -13,8 +15,10 @@ public partial class EntryReleaseItemViewModel : ActivatableViewModelBase
{ {
private readonly IWorkshopService _workshopService; private readonly IWorkshopService _workshopService;
private readonly IEntryDetails _entry; private readonly IEntryDetails _entry;
[Notify] private bool _isCurrentVersion; [Notify] private bool _isCurrentVersion;
[Notify] private string? _incompatibilityReason;
public EntryReleaseItemViewModel(IWorkshopService workshopService, IEntryDetails entry, IRelease release) public EntryReleaseItemViewModel(IWorkshopService workshopService, IEntryDetails entry, IRelease release)
{ {
_workshopService = workshopService; _workshopService = workshopService;
@ -33,6 +37,7 @@ public partial class EntryReleaseItemViewModel : ActivatableViewModelBase
}).DisposeWith(d); }).DisposeWith(d);
IsCurrentVersion = _workshopService.GetInstalledEntry(_entry.Id)?.ReleaseId == Release.Id; IsCurrentVersion = _workshopService.GetInstalledEntry(_entry.Id)?.ReleaseId == Release.Id;
IncompatibilityReason = !Release.IsCompatible() ? $"Requires Artemis v{Version.FromLong(Release.MinimumVersion!.Value)} or later" : null;
}); });
} }

View File

@ -24,22 +24,32 @@
<Button Command="{CompiledBinding Browse}" Margin="0 20">Browse file</Button> <Button Command="{CompiledBinding Browse}" Margin="0 20">Browse file</Button>
</StackPanel> </StackPanel>
<Border Grid.Row="1" Classes="card" ClipToBounds="True" IsVisible="{CompiledBinding SelectedPlugin, Converter={x:Static ObjectConverters.IsNotNull}}"> <Border Grid.Row="1" Classes="card" ClipToBounds="True" IsVisible="{CompiledBinding SelectedPlugin, Converter={x:Static ObjectConverters.IsNotNull}}">
<Grid RowDefinitions="30,30,30,30,Auto" ColumnDefinitions="200,Auto"> <StackPanel Orientation="Vertical" Spacing="5">
<TextBlock Grid.Row="0" Grid.Column="0" FontWeight="SemiBold">Path</TextBlock> <Grid ColumnDefinitions="200,*">
<TextBlock Grid.Row="0" Grid.Column="1" Text="{CompiledBinding Path}"></TextBlock> <TextBlock Grid.Column="0" FontWeight="SemiBold">Path</TextBlock>
<TextBlock Grid.Column="1" Text="{CompiledBinding Path}" TextWrapping="Wrap"></TextBlock>
<TextBlock Grid.Row="1" Grid.Column="0" FontWeight="SemiBold">Name</TextBlock> </Grid>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{CompiledBinding SelectedPlugin.Name}"></TextBlock> <Grid ColumnDefinitions="200,*">
<TextBlock Grid.Column="0" FontWeight="SemiBold">Name</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="0" FontWeight="SemiBold">Description</TextBlock> <TextBlock Grid.Column="1" Text="{CompiledBinding SelectedPlugin.Name}" TextWrapping="Wrap"></TextBlock>
<TextBlock Grid.Row="2" Grid.Column="1" Text="{CompiledBinding SelectedPlugin.Description}"></TextBlock> </Grid>
<Grid ColumnDefinitions="200,*">
<TextBlock Grid.Row="3" Grid.Column="0" FontWeight="SemiBold">Main entry point</TextBlock> <TextBlock Grid.Column="0" FontWeight="SemiBold">Description</TextBlock>
<TextBlock Grid.Row="3" Grid.Column="1" Text="{CompiledBinding SelectedPlugin.Main}"></TextBlock> <TextBlock Grid.Column="1" Text="{CompiledBinding SelectedPlugin.Description}" TextWrapping="Wrap"></TextBlock>
</Grid>
<TextBlock Grid.Row="4" Grid.Column="0" FontWeight="SemiBold">Version</TextBlock> <Grid ColumnDefinitions="200,*">
<TextBlock Grid.Row="4" Grid.Column="1" Text="{CompiledBinding SelectedPlugin.Version}"></TextBlock> <TextBlock Grid.Column="0" FontWeight="SemiBold">Main entry point</TextBlock>
</Grid> <TextBlock Grid.Column="1" Text="{CompiledBinding SelectedPlugin.Main}" TextWrapping="Wrap"></TextBlock>
</Grid>
<Grid ColumnDefinitions="200,*">
<TextBlock Grid.Column="0" FontWeight="SemiBold">Version</TextBlock>
<TextBlock Grid.Column="1" Text="{CompiledBinding SelectedPlugin.Version}" TextWrapping="Wrap"></TextBlock>
</Grid>
<Grid ColumnDefinitions="200,*" IsVisible="{CompiledBinding SelectedPlugin.MinimumVersion, Converter={x:Static ObjectConverters.IsNotNull}}">
<TextBlock Grid.Column="0" FontWeight="SemiBold">Min. Artemis version</TextBlock>
<TextBlock Grid.Column="1" Text="{CompiledBinding SelectedPlugin.MinimumVersion}" TextWrapping="Wrap"></TextBlock>
</Grid>
</StackPanel>
</Border> </Border>
</Grid> </Grid>

View File

@ -3,6 +3,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Extensions;
using Artemis.UI.Services.Interfaces; using Artemis.UI.Services.Interfaces;
using Artemis.UI.Shared.Utilities; using Artemis.UI.Shared.Utilities;
using Artemis.WebClient.Workshop; using Artemis.WebClient.Workshop;
@ -69,6 +70,12 @@ public class WorkshopUpdateService : IWorkshopUpdateService
if (latestRelease.Id == installedEntry.ReleaseId) if (latestRelease.Id == installedEntry.ReleaseId)
return false; return false;
if (!latestRelease.IsCompatible())
{
_logger.Information("Skipping auto-update of entry {Entry} because it requires a newer version of Artemis ({RequiredVersion})", entry, latestRelease.MinimumVersion);
return false;
}
_logger.Information("Auto-updating entry {Entry} to version {Version}", entry, latestRelease.Version); _logger.Information("Auto-updating entry {Entry} to version {Version}", entry, latestRelease.Version);
EntryInstallResult updateResult = await _workshopService.InstallEntry(entry, latestRelease, new Progress<StreamProgress>(), CancellationToken.None); EntryInstallResult updateResult = await _workshopService.InstallEntry(entry, latestRelease, new Progress<StreamProgress>(), CancellationToken.None);

View File

@ -15,6 +15,11 @@ public class EntryInstallResult
/// </summary> /// </summary>
public string? Message { get; private set; } public string? Message { get; private set; }
/// <summary>
/// Gets an exception thrown during the installation process, if any.
/// </summary>
public Exception? Exception { get; private set; }
/// <summary> /// <summary>
/// Gets the entry that was installed, if any. /// Gets the entry that was installed, if any.
/// </summary> /// </summary>
@ -34,6 +39,16 @@ public class EntryInstallResult
Message = message Message = message
}; };
} }
public static EntryInstallResult FromException(Exception exception)
{
return new EntryInstallResult
{
IsSuccess = false,
Message = exception.Message,
Exception = exception
};
}
public static EntryInstallResult FromSuccess(InstalledEntry installedEntry, object? result) public static EntryInstallResult FromSuccess(InstalledEntry installedEntry, object? result)
{ {

View File

@ -37,7 +37,7 @@ public class LayoutEntryInstallationHandler : IEntryInstallationHandler
} }
catch (Exception e) catch (Exception e)
{ {
return EntryInstallResult.FromFailure(e.Message); return EntryInstallResult.FromException(e);
} }
// Ensure there is an installed entry // Ensure there is an installed entry

View File

@ -51,7 +51,7 @@ public class PluginEntryInstallationHandler : IEntryInstallationHandler
} }
catch (Exception e) catch (Exception e)
{ {
return EntryInstallResult.FromFailure(e.Message); return EntryInstallResult.FromException(e);
} }
// Create the release directory // Create the release directory
@ -102,8 +102,9 @@ public class PluginEntryInstallationHandler : IEntryInstallationHandler
// ignored, will get cleaned up as an orphaned file // ignored, will get cleaned up as an orphaned file
} }
_workshopService.RemoveInstalledEntry(installedEntry); if (installedEntry.Entity.Id != Guid.Empty)
return EntryInstallResult.FromFailure(e.Message); _workshopService.RemoveInstalledEntry(installedEntry);
return EntryInstallResult.FromException(e);
} }
return ApplyAndSave(plugin, installedEntry, release); return ApplyAndSave(plugin, installedEntry, release);

View File

@ -32,7 +32,7 @@ public class ProfileEntryInstallationHandler : IEntryInstallationHandler
} }
catch (Exception e) catch (Exception e)
{ {
return EntryInstallResult.FromFailure(e.Message); return EntryInstallResult.FromException(e);
} }
// Find existing installation to potentially replace the profile // Find existing installation to potentially replace the profile

View File

@ -79,6 +79,7 @@ fragment entryDetails on Entry {
fragment release on Release { fragment release on Release {
id id
version version
minimumVersion
downloadSize downloadSize
md5Hash md5Hash
createdAt createdAt

View File

@ -138,8 +138,6 @@ type PluginInfo {
entryId: Long! entryId: Long!
entry: Entry! entry: Entry!
pluginGuid: UUID! pluginGuid: UUID!
api: Int
minmumVersion: String
website: String website: String
helpPage: String helpPage: String
repository: String repository: String
@ -253,6 +251,7 @@ type Release {
downloads: Long! downloads: Long!
downloadSize: Long! downloadSize: Long!
md5Hash: String md5Hash: String
minimumVersion: Long
entry: Entry! entry: Entry!
entryId: Long! entryId: Long!
dependencies: [Entry!]! dependencies: [Entry!]!
@ -532,8 +531,6 @@ input PluginInfoFilterInput {
entryId: LongOperationFilterInput entryId: LongOperationFilterInput
entry: EntryFilterInput entry: EntryFilterInput
pluginGuid: UuidOperationFilterInput pluginGuid: UuidOperationFilterInput
api: IntOperationFilterInput
minmumVersion: StringOperationFilterInput
website: StringOperationFilterInput website: StringOperationFilterInput
helpPage: StringOperationFilterInput helpPage: StringOperationFilterInput
repository: StringOperationFilterInput repository: StringOperationFilterInput
@ -547,8 +544,6 @@ input PluginInfoSortInput {
entryId: SortEnumType @cost(weight: "10") entryId: SortEnumType @cost(weight: "10")
entry: EntrySortInput @cost(weight: "10") entry: EntrySortInput @cost(weight: "10")
pluginGuid: SortEnumType @cost(weight: "10") pluginGuid: SortEnumType @cost(weight: "10")
api: SortEnumType @cost(weight: "10")
minmumVersion: SortEnumType @cost(weight: "10")
website: SortEnumType @cost(weight: "10") website: SortEnumType @cost(weight: "10")
helpPage: SortEnumType @cost(weight: "10") helpPage: SortEnumType @cost(weight: "10")
repository: SortEnumType @cost(weight: "10") repository: SortEnumType @cost(weight: "10")
@ -575,6 +570,7 @@ input ReleaseFilterInput {
downloads: LongOperationFilterInput downloads: LongOperationFilterInput
downloadSize: LongOperationFilterInput downloadSize: LongOperationFilterInput
md5Hash: StringOperationFilterInput md5Hash: StringOperationFilterInput
minimumVersion: LongOperationFilterInput
entry: EntryFilterInput entry: EntryFilterInput
entryId: LongOperationFilterInput entryId: LongOperationFilterInput
dependencies: ListFilterInputTypeOfEntryFilterInput dependencies: ListFilterInputTypeOfEntryFilterInput
@ -588,6 +584,7 @@ input ReleaseSortInput {
downloads: SortEnumType @cost(weight: "10") downloads: SortEnumType @cost(weight: "10")
downloadSize: SortEnumType @cost(weight: "10") downloadSize: SortEnumType @cost(weight: "10")
md5Hash: SortEnumType @cost(weight: "10") md5Hash: SortEnumType @cost(weight: "10")
minimumVersion: SortEnumType @cost(weight: "10")
entry: EntrySortInput @cost(weight: "10") entry: EntrySortInput @cost(weight: "10")
entryId: SortEnumType @cost(weight: "10") entryId: SortEnumType @cost(weight: "10")
} }