From 3952a268d12b9d66a1299b413599667aaca7c442 Mon Sep 17 00:00:00 2001 From: RobertBeekman Date: Fri, 2 Feb 2024 09:44:56 +0100 Subject: [PATCH 01/12] Debugger - Fixed current renderer always showing as Software Render service - Log which renderer is used Updating - Fixed release not loading when viewing it from the desktop notification Core - Removed old unused code Data model event cycle node - Fixed crash on shutdown --- src/Artemis.Core/Constants.cs | 6 - src/Artemis.Core/Models/Profile/Renderer.cs | 117 ------------------ src/Artemis.Core/Services/RenderService.cs | 1 + .../WindowsUpdateNotificationProvider.cs | 4 +- .../Performance/PerformanceDebugViewModel.cs | 2 +- .../Tabs/Render/RenderDebugViewModel.cs | 2 +- .../Steps/UploadStepViewModel.cs | 4 +- .../DataModel/DataModelEventCycleNode.cs | 21 +++- 8 files changed, 23 insertions(+), 134 deletions(-) delete mode 100644 src/Artemis.Core/Models/Profile/Renderer.cs diff --git a/src/Artemis.Core/Constants.cs b/src/Artemis.Core/Constants.cs index 8386f5fcf..ce374ca88 100644 --- a/src/Artemis.Core/Constants.cs +++ b/src/Artemis.Core/Constants.cs @@ -155,10 +155,4 @@ public static class Constants /// Gets the startup arguments provided to the application /// public static ReadOnlyCollection StartupArguments { get; set; } = null!; - - /// - /// Gets the graphics context to be used for rendering by SkiaSharp. - /// - public static IManagedGraphicsContext? ManagedGraphicsContext { get; internal set; } - } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/Renderer.cs b/src/Artemis.Core/Models/Profile/Renderer.cs deleted file mode 100644 index b72d48319..000000000 --- a/src/Artemis.Core/Models/Profile/Renderer.cs +++ /dev/null @@ -1,117 +0,0 @@ -using System; -using SkiaSharp; - -namespace Artemis.Core; - -internal class Renderer : IDisposable -{ - private bool _disposed; - private SKRect _lastBounds; - private GRContext? _lastGraphicsContext; - private SKRect _lastParentBounds; - private bool _valid; - public SKSurface? Surface { get; private set; } - public SKPaint? Paint { get; private set; } - public SKPath? Path { get; private set; } - public SKPoint TargetLocation { get; private set; } - - public bool IsOpen { get; private set; } - - /// - /// Opens the render context using the dimensions of the provided path - /// - public void Open(SKPath path, Folder? parent) - { - if (_disposed) - throw new ObjectDisposedException("Renderer"); - - if (IsOpen) - throw new ArtemisCoreException("Cannot open render context because it is already open"); - - if (path.Bounds != _lastBounds || (parent != null && parent.Bounds != _lastParentBounds) || _lastGraphicsContext != Constants.ManagedGraphicsContext?.GraphicsContext) - Invalidate(); - - if (!_valid || Surface == null) - { - SKRect pathBounds = path.Bounds; - int width = (int) pathBounds.Width; - int height = (int) pathBounds.Height; - - SKImageInfo imageInfo = new(width, height); - if (Constants.ManagedGraphicsContext?.GraphicsContext == null) - Surface = SKSurface.Create(imageInfo); - else - Surface = SKSurface.Create(Constants.ManagedGraphicsContext.GraphicsContext, true, imageInfo); - - Path = new SKPath(path); - Path.Transform(SKMatrix.CreateTranslation(pathBounds.Left * -1, pathBounds.Top * -1)); - - TargetLocation = new SKPoint(pathBounds.Location.X, pathBounds.Location.Y); - if (parent != null) - TargetLocation -= parent.Bounds.Location; - - Surface.Canvas.ClipPath(Path); - - _lastParentBounds = parent?.Bounds ?? new SKRect(); - _lastBounds = path.Bounds; - _lastGraphicsContext = Constants.ManagedGraphicsContext?.GraphicsContext; - _valid = true; - } - - Paint = new SKPaint(); - - Surface.Canvas.Clear(); - Surface.Canvas.Save(); - - IsOpen = true; - } - - public void Close() - { - if (_disposed) - throw new ObjectDisposedException("Renderer"); - - Surface?.Canvas.Restore(); - - // Looks like every part of the paint needs to be disposed :( - Paint?.ColorFilter?.Dispose(); - Paint?.ImageFilter?.Dispose(); - Paint?.MaskFilter?.Dispose(); - Paint?.PathEffect?.Dispose(); - Paint?.Dispose(); - - Paint = null; - - IsOpen = false; - } - - public void Invalidate() - { - if (_disposed) - throw new ObjectDisposedException("Renderer"); - - _valid = false; - } - - ~Renderer() - { - if (IsOpen) - Close(); - } - - public void Dispose() - { - if (IsOpen) - Close(); - - Surface?.Dispose(); - Paint?.Dispose(); - Path?.Dispose(); - - Surface = null; - Paint = null; - Path = null; - - _disposed = true; - } -} \ No newline at end of file diff --git a/src/Artemis.Core/Services/RenderService.cs b/src/Artemis.Core/Services/RenderService.cs index 26e23ca6f..bd73a0f0d 100644 --- a/src/Artemis.Core/Services/RenderService.cs +++ b/src/Artemis.Core/Services/RenderService.cs @@ -123,6 +123,7 @@ internal class RenderService : IRenderService, IRenderer, IDisposable return; } + _logger.Information("Applying {Name} graphics context", _preferredGraphicsContext.Value); if (_preferredGraphicsContext.Value == "Software") { GraphicsContext = null; diff --git a/src/Artemis.UI.Windows/Providers/WindowsUpdateNotificationProvider.cs b/src/Artemis.UI.Windows/Providers/WindowsUpdateNotificationProvider.cs index 8ca86dd87..ed241cf62 100644 --- a/src/Artemis.UI.Windows/Providers/WindowsUpdateNotificationProvider.cs +++ b/src/Artemis.UI.Windows/Providers/WindowsUpdateNotificationProvider.cs @@ -60,10 +60,10 @@ public class WindowsUpdateNotificationProvider : IUpdateNotificationProvider Dispatcher.UIThread.Invoke(async () => { _mainWindowService.OpenMainWindow(); - if (releaseId != null) + if (releaseId != null && releaseId.Value != Guid.Empty) await _router.Navigate($"settings/releases/{releaseId}"); else - await _router.Navigate($"settings/releases"); + await _router.Navigate("settings/releases"); }); } diff --git a/src/Artemis.UI/Screens/Debugger/Tabs/Performance/PerformanceDebugViewModel.cs b/src/Artemis.UI/Screens/Debugger/Tabs/Performance/PerformanceDebugViewModel.cs index cefb569dc..0b2bd65a9 100644 --- a/src/Artemis.UI/Screens/Debugger/Tabs/Performance/PerformanceDebugViewModel.cs +++ b/src/Artemis.UI/Screens/Debugger/Tabs/Performance/PerformanceDebugViewModel.cs @@ -63,7 +63,7 @@ public partial class PerformanceDebugViewModel : ActivatableViewModelBase private void HandleActivation() { - Renderer = Constants.ManagedGraphicsContext != null ? Constants.ManagedGraphicsContext.GetType().Name : "Software"; + Renderer = _renderService.GraphicsContext?.GetType().Name ?? "Software"; _renderService.FrameRendered += RenderServiceOnFrameRendered; } diff --git a/src/Artemis.UI/Screens/Debugger/Tabs/Render/RenderDebugViewModel.cs b/src/Artemis.UI/Screens/Debugger/Tabs/Render/RenderDebugViewModel.cs index b4594eee3..dbf76e823 100644 --- a/src/Artemis.UI/Screens/Debugger/Tabs/Render/RenderDebugViewModel.cs +++ b/src/Artemis.UI/Screens/Debugger/Tabs/Render/RenderDebugViewModel.cs @@ -35,7 +35,7 @@ public partial class RenderDebugViewModel : ActivatableViewModelBase private void HandleActivation() { - Renderer = Constants.ManagedGraphicsContext != null ? Constants.ManagedGraphicsContext.GetType().Name : "Software"; + Renderer = _renderService.GraphicsContext?.GetType().Name ?? "Software"; _renderService.FrameRendered += RenderServiceOnFrameRendered; } diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/UploadStepViewModel.cs b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/UploadStepViewModel.cs index 2a21cde43..04eae8c0f 100644 --- a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/UploadStepViewModel.cs +++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/UploadStepViewModel.cs @@ -82,8 +82,8 @@ public partial class UploadStepViewModel : SubmissionViewModel FailureMessage = e.Message; Failed = true; - // If something went wrong halfway through, delete the entry - if (_entryId != null) + // If something went wrong halfway through, delete the entry if it was new + if (State.EntryId == null && _entryId != null) await _workshopClient.RemoveEntry.ExecuteAsync(_entryId.Value, CancellationToken.None); } finally diff --git a/src/Artemis.VisualScripting/Nodes/DataModel/DataModelEventCycleNode.cs b/src/Artemis.VisualScripting/Nodes/DataModel/DataModelEventCycleNode.cs index b0c8b684a..6ab264a77 100644 --- a/src/Artemis.VisualScripting/Nodes/DataModel/DataModelEventCycleNode.cs +++ b/src/Artemis.VisualScripting/Nodes/DataModel/DataModelEventCycleNode.cs @@ -11,6 +11,7 @@ public class DataModelEventCycleNode : Node public void Dispose() { - if (_dataModelPath?.GetValue() is IDataModelEvent newEvent) - newEvent.EventTriggered -= OnEventTriggered; + if (_subscribedEvent != null) + { + _subscribedEvent.EventTriggered -= OnEventTriggered; + _subscribedEvent = null; + } + _dataModelPath?.Dispose(); } } \ No newline at end of file From a2fe5d5d083fffa301c3c76b383f3e37dcc19e53 Mon Sep 17 00:00:00 2001 From: RobertBeekman Date: Thu, 8 Feb 2024 20:58:16 +0100 Subject: [PATCH 02/12] Settings - Added account management --- src/Artemis.UI/Routing/Routes.cs | 1 + .../LayoutProviders/WorkshopLayoutView.axaml | 5 +- .../WorkshopLayoutViewModel.cs | 1 + .../Account/ChangeEmailAddressView.axaml | 14 ++ .../Account/ChangeEmailAddressView.axaml.cs | 16 +++ .../Account/ChangeEmailAddressViewModel.cs | 50 ++++++++ .../Settings/Account/ChangePasswordView.axaml | 17 +++ .../Account/ChangePasswordView.axaml.cs | 17 +++ .../Account/ChangePasswordViewModel.cs | 47 +++++++ .../Settings/Account/RemoveAccountView.axaml | 14 ++ .../Account/RemoveAccountView.axaml.cs | 16 +++ .../Account/RemoveAccountViewModel.cs | 52 ++++++++ .../Screens/Settings/SettingsViewModel.cs | 1 + .../Settings/Tabs/AccountTabView.axaml | 121 ++++++++++++++++++ .../Settings/Tabs/AccountTabView.axaml.cs | 14 ++ .../Settings/Tabs/AccountTabViewModel.cs | 105 +++++++++++++++ .../CurrentUser/CurrentUserView.axaml | 18 ++- .../CurrentUser/CurrentUserView.axaml.cs | 8 +- .../CurrentUser/CurrentUserViewModel.cs | 10 +- .../Entries/Details/EntryReleasesViewModel.cs | 1 + .../Dialogs/DeviceSelectionDialogViewModel.cs | 1 + .../Workshop/Layout/LayoutDetailsViewModel.cs | 1 + .../Library/SubmissionDetailViewModel.cs | 2 +- .../Library/Tabs/InstalledTabItemViewModel.cs | 1 + .../Library/Tabs/InstalledTabViewModel.cs | 1 + .../Steps/UploadStepViewModel.cs | 4 +- .../DryIoc/ContainerExtensions.cs | 8 +- .../EntryInstallResult.cs | 3 +- .../IEntryInstallationHandler.cs | 1 + .../LayoutEntryInstallationHandler.cs | 1 + .../ProfileEntryInstallationHandler.cs | 1 + .../{ImageUploadResult.cs => ApiResult.cs} | 10 +- .../{Services => Models}/AccessToken.cs | 3 +- .../{Services => Models}/InstalledEntry.cs | 2 +- .../Models/PersonalAccessToken.cs | 3 + .../Providers/WorkshopLayoutProvider.cs | 1 + .../Services/AuthenticationService.cs | 1 + .../Interfaces/IUserManagementService.cs | 16 +++ .../Services/Interfaces/IWorkshopService.cs | 5 +- .../Services/UserManagementService.cs | 103 +++++++++++++++ .../Services/WorkshopService.cs | 14 +- .../WorkshopConstants.cs | 1 + 42 files changed, 681 insertions(+), 30 deletions(-) create mode 100644 src/Artemis.UI/Screens/Settings/Account/ChangeEmailAddressView.axaml create mode 100644 src/Artemis.UI/Screens/Settings/Account/ChangeEmailAddressView.axaml.cs create mode 100644 src/Artemis.UI/Screens/Settings/Account/ChangeEmailAddressViewModel.cs create mode 100644 src/Artemis.UI/Screens/Settings/Account/ChangePasswordView.axaml create mode 100644 src/Artemis.UI/Screens/Settings/Account/ChangePasswordView.axaml.cs create mode 100644 src/Artemis.UI/Screens/Settings/Account/ChangePasswordViewModel.cs create mode 100644 src/Artemis.UI/Screens/Settings/Account/RemoveAccountView.axaml create mode 100644 src/Artemis.UI/Screens/Settings/Account/RemoveAccountView.axaml.cs create mode 100644 src/Artemis.UI/Screens/Settings/Account/RemoveAccountViewModel.cs create mode 100644 src/Artemis.UI/Screens/Settings/Tabs/AccountTabView.axaml create mode 100644 src/Artemis.UI/Screens/Settings/Tabs/AccountTabView.axaml.cs create mode 100644 src/Artemis.UI/Screens/Settings/Tabs/AccountTabViewModel.cs rename src/Artemis.WebClient.Workshop/Handlers/UploadHandlers/{ImageUploadResult.cs => ApiResult.cs} (51%) rename src/Artemis.WebClient.Workshop/{Services => Models}/AccessToken.cs (92%) rename src/Artemis.WebClient.Workshop/{Services => Models}/InstalledEntry.cs (99%) create mode 100644 src/Artemis.WebClient.Workshop/Models/PersonalAccessToken.cs create mode 100644 src/Artemis.WebClient.Workshop/Services/Interfaces/IUserManagementService.cs create mode 100644 src/Artemis.WebClient.Workshop/Services/UserManagementService.cs diff --git a/src/Artemis.UI/Routing/Routes.cs b/src/Artemis.UI/Routing/Routes.cs index e129b384c..1d4862f44 100644 --- a/src/Artemis.UI/Routing/Routes.cs +++ b/src/Artemis.UI/Routing/Routes.cs @@ -64,6 +64,7 @@ public static class Routes new RouteRegistration("{releaseId:guid}") } }, + new RouteRegistration("account"), new RouteRegistration("about") } }, diff --git a/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/WorkshopLayoutView.axaml b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/WorkshopLayoutView.axaml index 07a8a7325..38c1e2eef 100644 --- a/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/WorkshopLayoutView.axaml +++ b/src/Artemis.UI/Screens/Device/Tabs/Layout/LayoutProviders/WorkshopLayoutView.axaml @@ -4,6 +4,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:services="clr-namespace:Artemis.WebClient.Workshop.Services;assembly=Artemis.WebClient.Workshop" xmlns:layoutProviders="clr-namespace:Artemis.UI.Screens.Device.Layout.LayoutProviders" + xmlns:models="clr-namespace:Artemis.WebClient.Workshop.Models;assembly=Artemis.WebClient.Workshop" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="Artemis.UI.Screens.Device.Layout.LayoutProviders.WorkshopLayoutView" x:DataType="layoutProviders:WorkshopLayoutViewModel"> @@ -19,7 +20,7 @@ + + + You are not logged in + + In order to manage your account you must be logged in. + + Creating an account is free and we'll not bother you with a newsletter or crap like that. + + + + + + Click Log In below to (create an account) and log in. + + You'll also be able to log in with Google or Discord. + + + + + + + + + + + + + + + + + + + + Account management + + + + + + + Change avatar + + + + - + - + @@ -45,7 +46,7 @@ - + @@ -54,11 +55,27 @@ + + + + Credentials + + + + + + + + Path + + + Name + + + Description + + + Main entry point + + + Version + + + + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Plugin/PluginSelectionStepView.axaml.cs b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Plugin/PluginSelectionStepView.axaml.cs new file mode 100644 index 000000000..79866d0e5 --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Plugin/PluginSelectionStepView.axaml.cs @@ -0,0 +1,11 @@ +using Avalonia.ReactiveUI; + +namespace Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.Plugin; + +public partial class PluginSelectionStepView : ReactiveUserControl +{ + public PluginSelectionStepView() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Plugin/PluginSelectionStepViewModel.cs b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Plugin/PluginSelectionStepViewModel.cs new file mode 100644 index 000000000..a44a7e05b --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Plugin/PluginSelectionStepViewModel.cs @@ -0,0 +1,83 @@ +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Reactive; +using System.Reactive.Disposables; +using System.Reactive.Linq; +using System.Threading.Tasks; +using Artemis.Core; +using Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.Profile; +using Artemis.UI.Shared.Services; +using Artemis.WebClient.Workshop.Handlers.UploadHandlers; +using PropertyChanged.SourceGenerator; +using ReactiveUI; + +namespace Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.Plugin; + +public partial class PluginSelectionStepViewModel : SubmissionViewModel +{ + private readonly IWindowService _windowService; + [Notify] private PluginInfo? _selectedPlugin; + [Notify] private string? _path; + + /// + public PluginSelectionStepViewModel(IWindowService windowService) + { + _windowService = windowService; + + GoBack = ReactiveCommand.Create(() => State.ChangeScreen()); + Continue = ReactiveCommand.Create(ExecuteContinue, this.WhenAnyValue(vm => vm.SelectedPlugin).Select(p => p != null)); + Browse = ReactiveCommand.CreateFromTask(ExecuteBrowse); + + this.WhenActivated((CompositeDisposable _) => + { + ShowGoBack = State.EntryId == null; + if (State.EntrySource is PluginEntrySource pluginEntrySource) + { + SelectedPlugin = pluginEntrySource.PluginInfo; + Path = pluginEntrySource.Path; + } + }); + } + + public ReactiveCommand Browse { get; } + + private async Task ExecuteBrowse() + { + string[]? files = await _windowService.CreateOpenFileDialog().HavingFilter(f => f.WithExtension("zip").WithName("ZIP files")).ShowAsync(); + if (files == null) + return; + + // Find the metadata file in the zip + using ZipArchive archive = ZipFile.OpenRead(files[0]); + ZipArchiveEntry? metaDataFileEntry = archive.Entries.FirstOrDefault(e => e.Name == "plugin.json"); + if (metaDataFileEntry == null) + throw new ArtemisPluginException("Couldn't find a plugin.json in " + files[0]); + + using StreamReader reader = new(metaDataFileEntry.Open()); + PluginInfo pluginInfo = CoreJson.DeserializeObject(reader.ReadToEnd())!; + if (!pluginInfo.Main.EndsWith(".dll")) + throw new ArtemisPluginException("Main entry in plugin.json must point to a .dll file"); + + SelectedPlugin = pluginInfo; + Path = files[0]; + } + + private void ExecuteContinue() + { + if (SelectedPlugin == null || Path == null) + return; + + State.EntrySource = new PluginEntrySource(SelectedPlugin, Path); + + if (string.IsNullOrWhiteSpace(State.Name)) + State.Name = SelectedPlugin.Name; + if (string.IsNullOrWhiteSpace(State.Summary)) + State.Summary = SelectedPlugin.Description ?? ""; + + if (State.EntryId == null) + State.ChangeScreen(); + else + State.ChangeScreen(); + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/SpecificationsStepViewModel.cs b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/SpecificationsStepViewModel.cs index bf9ea0f6e..678aea918 100644 --- a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/SpecificationsStepViewModel.cs +++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/SpecificationsStepViewModel.cs @@ -6,6 +6,7 @@ using System.Reactive.Disposables; using Artemis.UI.Extensions; using Artemis.UI.Screens.Workshop.Entries; using Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.Layout; +using Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.Plugin; using Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.Profile; using Artemis.WebClient.Workshop; using DynamicData; @@ -37,19 +38,14 @@ public partial class SpecificationsStepViewModel : SubmissionViewModel // Apply what's there so far ApplyToState(); - switch (State.EntryType) - { - case EntryType.Layout: - State.ChangeScreen(); - break; - case EntryType.Plugin: - break; - case EntryType.Profile: - State.ChangeScreen(); - break; - default: - throw new ArgumentOutOfRangeException(); - } + if (State.EntryType == EntryType.Layout) + State.ChangeScreen(); + else if (State.EntryType == EntryType.Plugin) + State.ChangeScreen(); + else if (State.EntryType == EntryType.Profile) + State.ChangeScreen(); + else + throw new ArgumentOutOfRangeException(); } private void ExecuteContinue() diff --git a/src/Artemis.WebClient.Workshop/Handlers/UploadHandlers/EntryUploadHandlerFactory.cs b/src/Artemis.WebClient.Workshop/Handlers/UploadHandlers/EntryUploadHandlerFactory.cs index 809c1c16b..8bc2621ea 100644 --- a/src/Artemis.WebClient.Workshop/Handlers/UploadHandlers/EntryUploadHandlerFactory.cs +++ b/src/Artemis.WebClient.Workshop/Handlers/UploadHandlers/EntryUploadHandlerFactory.cs @@ -15,6 +15,7 @@ public class EntryUploadHandlerFactory { return entryType switch { + EntryType.Plugin => _container.Resolve(), EntryType.Profile => _container.Resolve(), EntryType.Layout => _container.Resolve(), _ => throw new NotSupportedException($"EntryType '{entryType}' is not supported.") diff --git a/src/Artemis.WebClient.Workshop/Handlers/UploadHandlers/Implementations/PluginEntrySource.cs b/src/Artemis.WebClient.Workshop/Handlers/UploadHandlers/Implementations/PluginEntrySource.cs new file mode 100644 index 000000000..553a7f394 --- /dev/null +++ b/src/Artemis.WebClient.Workshop/Handlers/UploadHandlers/Implementations/PluginEntrySource.cs @@ -0,0 +1,15 @@ +using Artemis.Core; + +namespace Artemis.WebClient.Workshop.Handlers.UploadHandlers; + +public class PluginEntrySource : IEntrySource +{ + public PluginEntrySource(PluginInfo pluginInfo, string path) + { + PluginInfo = pluginInfo; + Path = path; + } + + public PluginInfo PluginInfo { get; set; } + public string Path { get; set; } +} \ No newline at end of file diff --git a/src/Artemis.WebClient.Workshop/Handlers/UploadHandlers/Implementations/PluginEntryUploadHandler.cs b/src/Artemis.WebClient.Workshop/Handlers/UploadHandlers/Implementations/PluginEntryUploadHandler.cs new file mode 100644 index 000000000..50fa0a283 --- /dev/null +++ b/src/Artemis.WebClient.Workshop/Handlers/UploadHandlers/Implementations/PluginEntryUploadHandler.cs @@ -0,0 +1,40 @@ +using System.Net.Http.Headers; +using Artemis.WebClient.Workshop.Entities; +using Newtonsoft.Json; + +namespace Artemis.WebClient.Workshop.Handlers.UploadHandlers; + +public class PluginEntryUploadHandler : IEntryUploadHandler +{ + private readonly IHttpClientFactory _httpClientFactory; + + public PluginEntryUploadHandler(IHttpClientFactory httpClientFactory) + { + _httpClientFactory = httpClientFactory; + } + + /// + public async Task CreateReleaseAsync(long entryId, IEntrySource entrySource, CancellationToken cancellationToken) + { + if (entrySource is not PluginEntrySource source) + throw new InvalidOperationException("Can only create releases for plugins"); + + // Submit the archive + HttpClient client = _httpClientFactory.CreateClient(WorkshopConstants.WORKSHOP_CLIENT_NAME); + + // Construct the request + await using FileStream fileStream = File.Open(source.Path, FileMode.Open); + MultipartFormDataContent content = new(); + StreamContent streamContent = new(fileStream); + streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/zip"); + content.Add(streamContent, "file", "file.zip"); + + // Submit + HttpResponseMessage response = await client.PostAsync("releases/upload/" + entryId, content, cancellationToken); + if (!response.IsSuccessStatusCode) + return EntryUploadResult.FromFailure($"{response.StatusCode} - {await response.Content.ReadAsStringAsync(cancellationToken)}"); + + Release? release = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync(cancellationToken)); + return release != null ? EntryUploadResult.FromSuccess(release) : EntryUploadResult.FromFailure("Failed to deserialize response"); + } +} \ No newline at end of file From 21b8112de58deb72d8c2d886671a516446b9f4da Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 15 Feb 2024 22:57:33 +0100 Subject: [PATCH 05/12] Workshop - Implemented plugin browsing, installation and removal --- src/Artemis.UI/Artemis.UI.csproj | 20 --- src/Artemis.UI/ArtemisBootstrapper.cs | 1 + src/Artemis.UI/Routing/Routes.cs | 3 + src/Artemis.UI/Screens/Root/RootViewModel.cs | 5 + .../Screens/Sidebar/SidebarViewModel.cs | 1 + .../Workshop/Entries/EntriesViewModel.cs | 3 +- .../Entries/List/EntryListItemViewModel.cs | 1 + .../Entries/Tabs/PluginListView.axaml | 65 ++++++++++ .../Entries/Tabs/PluginListView.axaml.cs | 14 ++ .../Entries/Tabs/PluginListViewModel.cs | 34 +++++ .../Entries/Tabs/ProfileListViewModel.cs | 2 +- .../Workshop/Home/WorkshopHomeView.axaml | 8 ++ .../Workshop/Plugin/PluginDetailsView.axaml | 30 +++++ .../Plugin/PluginDetailsView.axaml.cs | 14 ++ .../Workshop/Plugin/PluginDetailsViewModel.cs | 59 +++++++++ .../EntryInstallationHandlerFactory.cs | 1 + .../EntryUninstallResult.cs | 5 +- .../PluginEntryInstallationHandler.cs | 122 ++++++++++++++++++ .../Services/Interfaces/IWorkshopService.cs | 1 + .../Services/WorkshopService.cs | 48 ++++++- .../WorkshopConstants.cs | 8 +- 21 files changed, 410 insertions(+), 35 deletions(-) create mode 100644 src/Artemis.UI/Screens/Workshop/Entries/Tabs/PluginListView.axaml create mode 100644 src/Artemis.UI/Screens/Workshop/Entries/Tabs/PluginListView.axaml.cs create mode 100644 src/Artemis.UI/Screens/Workshop/Entries/Tabs/PluginListViewModel.cs create mode 100644 src/Artemis.UI/Screens/Workshop/Plugin/PluginDetailsView.axaml create mode 100644 src/Artemis.UI/Screens/Workshop/Plugin/PluginDetailsView.axaml.cs create mode 100644 src/Artemis.UI/Screens/Workshop/Plugin/PluginDetailsViewModel.cs create mode 100644 src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/PluginEntryInstallationHandler.cs diff --git a/src/Artemis.UI/Artemis.UI.csproj b/src/Artemis.UI/Artemis.UI.csproj index 3f5b1f056..0048cffb7 100644 --- a/src/Artemis.UI/Artemis.UI.csproj +++ b/src/Artemis.UI/Artemis.UI.csproj @@ -35,24 +35,4 @@ - - - - PluginSelectionStepView.axaml - Code - - - ProfileAdaptionHintsStepView.axaml - Code - - - ProfileSelectionStepView.axaml - Code - - - - - - - \ No newline at end of file diff --git a/src/Artemis.UI/ArtemisBootstrapper.cs b/src/Artemis.UI/ArtemisBootstrapper.cs index be365cd9f..f869bb8e3 100644 --- a/src/Artemis.UI/ArtemisBootstrapper.cs +++ b/src/Artemis.UI/ArtemisBootstrapper.cs @@ -13,6 +13,7 @@ using Artemis.UI.Shared.Services; using Artemis.VisualScripting.DryIoc; using Artemis.WebClient.Updating.DryIoc; using Artemis.WebClient.Workshop.DryIoc; +using Artemis.WebClient.Workshop.Services; using Avalonia; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; diff --git a/src/Artemis.UI/Routing/Routes.cs b/src/Artemis.UI/Routing/Routes.cs index 1d4862f44..8e26f8d3b 100644 --- a/src/Artemis.UI/Routing/Routes.cs +++ b/src/Artemis.UI/Routing/Routes.cs @@ -12,6 +12,7 @@ using Artemis.UI.Screens.Workshop.Home; using Artemis.UI.Screens.Workshop.Layout; using Artemis.UI.Screens.Workshop.Library; using Artemis.UI.Screens.Workshop.Library.Tabs; +using Artemis.UI.Screens.Workshop.Plugin; using Artemis.UI.Screens.Workshop.Profile; using Artemis.UI.Shared.Routing; @@ -32,6 +33,8 @@ public static class Routes { Children = new List { + new RouteRegistration("plugins/{page:int}"), + new RouteRegistration("plugins/details/{entryId:long}"), new RouteRegistration("profiles/{page:int}"), new RouteRegistration("profiles/details/{entryId:long}"), new RouteRegistration("layouts/{page:int}"), diff --git a/src/Artemis.UI/Screens/Root/RootViewModel.cs b/src/Artemis.UI/Screens/Root/RootViewModel.cs index e1fb3eeb9..ce83f0742 100644 --- a/src/Artemis.UI/Screens/Root/RootViewModel.cs +++ b/src/Artemis.UI/Screens/Root/RootViewModel.cs @@ -13,6 +13,7 @@ using Artemis.UI.Shared; using Artemis.UI.Shared.Routing; using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services.MainWindow; +using Artemis.WebClient.Workshop.Services; using Avalonia; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; @@ -41,6 +42,7 @@ public class RootViewModel : RoutableHostScreen, IMainWindowProv IMainWindowService mainWindowService, IDebugService debugService, IUpdateService updateService, + IWorkshopService workshopService, SidebarViewModel sidebarViewModel, DefaultTitleBarViewModel defaultTitleBarViewModel) { @@ -76,6 +78,9 @@ public class RootViewModel : RoutableHostScreen, IMainWindowProv { if (_updateService.Initialize()) return; + + // Before initializing the core and files become in use, clean up orphaned files + workshopService.RemoveOrphanedFiles(); coreService.Initialize(); registrationService.RegisterBuiltInDataModelDisplays(); diff --git a/src/Artemis.UI/Screens/Sidebar/SidebarViewModel.cs b/src/Artemis.UI/Screens/Sidebar/SidebarViewModel.cs index ae9de0d2c..bb2d56f95 100644 --- a/src/Artemis.UI/Screens/Sidebar/SidebarViewModel.cs +++ b/src/Artemis.UI/Screens/Sidebar/SidebarViewModel.cs @@ -43,6 +43,7 @@ public partial class SidebarViewModel : ActivatableViewModelBase { new(MaterialIconKind.FolderVideo, "Profiles", "workshop/entries/profiles/1", "workshop/entries/profiles"), new(MaterialIconKind.KeyboardVariant, "Layouts", "workshop/entries/layouts/1", "workshop/entries/layouts"), + new(MaterialIconKind.Plugin, "Plugins", "workshop/entries/plugins/1", "workshop/entries/plugins"), new(MaterialIconKind.Bookshelf, "Library", "workshop/library"), }), diff --git a/src/Artemis.UI/Screens/Workshop/Entries/EntriesViewModel.cs b/src/Artemis.UI/Screens/Workshop/Entries/EntriesViewModel.cs index 4ee34fee4..904348bc6 100644 --- a/src/Artemis.UI/Screens/Workshop/Entries/EntriesViewModel.cs +++ b/src/Artemis.UI/Screens/Workshop/Entries/EntriesViewModel.cs @@ -25,7 +25,8 @@ public partial class EntriesViewModel : RoutableHostScreen Tabs = new ObservableCollection { new("Profiles", "workshop/entries/profiles/1", "workshop/entries/profiles"), - new("Layouts", "workshop/entries/layouts/1", "workshop/entries/layouts") + new("Layouts", "workshop/entries/layouts/1", "workshop/entries/layouts"), + new("Plugins", "workshop/entries/plugins/1", "workshop/entries/plugins"), }; this.WhenActivated(d => diff --git a/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListItemViewModel.cs b/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListItemViewModel.cs index be57fd898..757396c72 100644 --- a/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListItemViewModel.cs +++ b/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListItemViewModel.cs @@ -34,6 +34,7 @@ public class EntryListItemViewModel : ActivatableViewModelBase await _router.Navigate($"workshop/entries/profiles/details/{Entry.Id}"); break; case EntryType.Plugin: + await _router.Navigate($"workshop/entries/plugins/details/{Entry.Id}"); break; default: throw new ArgumentOutOfRangeException(); diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Tabs/PluginListView.axaml b/src/Artemis.UI/Screens/Workshop/Entries/Tabs/PluginListView.axaml new file mode 100644 index 000000000..e52c30641 --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Entries/Tabs/PluginListView.axaml @@ -0,0 +1,65 @@ + + + + + + + + + + + Categories + + + + + + + + + + + + + + + + + + + + + + + + + + + Looks like your current filters gave no results + + Modify or clear your filters to view other plugins + + + + + + + + diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Tabs/PluginListView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Entries/Tabs/PluginListView.axaml.cs new file mode 100644 index 000000000..2e32eea93 --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Entries/Tabs/PluginListView.axaml.cs @@ -0,0 +1,14 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using Avalonia.ReactiveUI; + +namespace Artemis.UI.Screens.Workshop.Entries.Tabs; + +public partial class PluginListView : ReactiveUserControl +{ + public PluginListView() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Tabs/PluginListViewModel.cs b/src/Artemis.UI/Screens/Workshop/Entries/Tabs/PluginListViewModel.cs new file mode 100644 index 000000000..206697490 --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Entries/Tabs/PluginListViewModel.cs @@ -0,0 +1,34 @@ +using System; +using Artemis.UI.Screens.Workshop.Categories; +using Artemis.UI.Screens.Workshop.Entries.List; +using Artemis.UI.Shared.Routing; +using Artemis.UI.Shared.Services; +using Artemis.WebClient.Workshop; + +namespace Artemis.UI.Screens.Workshop.Entries.Tabs; + +public class PluginListViewModel : EntryListViewModel +{ + public PluginListViewModel(IWorkshopClient workshopClient, + IRouter router, + CategoriesViewModel categoriesViewModel, + EntryListInputViewModel entryListInputViewModel, + INotificationService notificationService, + Func getEntryListViewModel) + : base("workshop/entries/plugins", workshopClient, router, categoriesViewModel, entryListInputViewModel, notificationService, getEntryListViewModel) + { + entryListInputViewModel.SearchWatermark = "Search plugins"; + } + + protected override EntryFilterInput GetFilter() + { + return new EntryFilterInput + { + And = new[] + { + new EntryFilterInput {EntryType = new EntryTypeOperationFilterInput {Eq = EntryType.Plugin}}, + base.GetFilter() + } + }; + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Tabs/ProfileListViewModel.cs b/src/Artemis.UI/Screens/Workshop/Entries/Tabs/ProfileListViewModel.cs index c09a587a3..f251ee10d 100644 --- a/src/Artemis.UI/Screens/Workshop/Entries/Tabs/ProfileListViewModel.cs +++ b/src/Artemis.UI/Screens/Workshop/Entries/Tabs/ProfileListViewModel.cs @@ -12,7 +12,7 @@ public class ProfileListViewModel : List.EntryListViewModel public ProfileListViewModel(IWorkshopClient workshopClient, IRouter router, CategoriesViewModel categoriesViewModel, - List.EntryListInputViewModel entryListInputViewModel, + EntryListInputViewModel entryListInputViewModel, INotificationService notificationService, Func getEntryListViewModel) : base("workshop/entries/profiles", workshopClient, router, categoriesViewModel, entryListInputViewModel, notificationService, getEntryListViewModel) diff --git a/src/Artemis.UI/Screens/Workshop/Home/WorkshopHomeView.axaml b/src/Artemis.UI/Screens/Workshop/Home/WorkshopHomeView.axaml index 433f87c50..4bc7bd2a4 100644 --- a/src/Artemis.UI/Screens/Workshop/Home/WorkshopHomeView.axaml +++ b/src/Artemis.UI/Screens/Workshop/Home/WorkshopHomeView.axaml @@ -57,6 +57,14 @@ + + - + + + + + + + + + + + + + + + + + + + + + + + + + @@ -46,7 +51,7 @@ - + @@ -66,8 +71,8 @@ + + Description + Created at + Expires at + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Settings/Tabs/AccountTabViewModel.cs b/src/Artemis.UI/Screens/Settings/Tabs/AccountTabViewModel.cs index 0e361deb3..afab57079 100644 --- a/src/Artemis.UI/Screens/Settings/Tabs/AccountTabViewModel.cs +++ b/src/Artemis.UI/Screens/Settings/Tabs/AccountTabViewModel.cs @@ -1,7 +1,10 @@ using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; using System.ComponentModel; using System.IO; using System.Linq; +using System.Reactive; using System.Reactive.Disposables; using System.Reactive.Linq; using System.Threading; @@ -12,6 +15,7 @@ using Artemis.UI.Shared.Routing; using Artemis.UI.Shared.Services; using Artemis.WebClient.Workshop; using Artemis.WebClient.Workshop.Handlers.UploadHandlers; +using Artemis.WebClient.Workshop.Models; using Artemis.WebClient.Workshop.Services; using IdentityModel; using PropertyChanged.SourceGenerator; @@ -29,6 +33,7 @@ public partial class AccountTabViewModel : RoutableScreen [Notify(Setter.Private)] private string? _name; [Notify(Setter.Private)] private string? _email; [Notify(Setter.Private)] private string? _avatarUrl; + [Notify(Setter.Private)] private ObservableCollection _personalAccessTokens = new(); public AccountTabViewModel(IWindowService windowService, IAuthenticationService authenticationService, IUserManagementService userManagementService) { @@ -36,10 +41,11 @@ public partial class AccountTabViewModel : RoutableScreen _authenticationService = authenticationService; _userManagementService = userManagementService; _authenticationService.AutoLogin(true); - + DisplayName = "Account"; IsLoggedIn = _authenticationService.IsLoggedIn; - + DeleteToken = ReactiveCommand.CreateFromTask(ExecuteDeleteToken); + this.WhenActivated(d => { _canChangePassword = _authenticationService.GetClaim(JwtClaimTypes.AuthenticationMethod).Select(c => c?.Value == "pwd").ToProperty(this, vm => vm.CanChangePassword); @@ -50,12 +56,13 @@ public partial class AccountTabViewModel : RoutableScreen public bool CanChangePassword => _canChangePassword?.Value ?? false; public IObservable IsLoggedIn { get; } + public ReactiveCommand DeleteToken { get; } public async Task Login() { await _windowService.CreateContentDialog().WithViewModel(out WorkshopLoginViewModel _).WithTitle("Account login").ShowAsync(); } - + public async Task ChangeAvatar() { string[]? result = await _windowService.CreateOpenFileDialog().HavingFilter(f => f.WithBitmaps()).ShowAsync(); @@ -85,7 +92,7 @@ public partial class AccountTabViewModel : RoutableScreen .HavingPrimaryButton(b => b.WithText("Submit").WithCommand(vm.Submit)) .ShowAsync(); } - + public async Task ChangePasswordAddress() { await _windowService.CreateContentDialog().WithTitle("Change password") @@ -94,7 +101,7 @@ public partial class AccountTabViewModel : RoutableScreen .HavingPrimaryButton(b => b.WithText("Submit").WithCommand(vm.Submit)) .ShowAsync(); } - + public async Task RemoveAccount() { await _windowService.CreateContentDialog().WithTitle("Remove account") @@ -104,11 +111,36 @@ public partial class AccountTabViewModel : RoutableScreen .ShowAsync(); } - private void LoadCurrentUser() + public async Task GenerateToken() + { + await _windowService.CreateContentDialog().WithTitle("Create Personal Access Token") + .WithViewModel(out CreatePersonalAccessTokenViewModel vm) + .WithCloseButtonText("Cancel") + .HavingPrimaryButton(b => b.WithText("Submit").WithCommand(vm.Submit)) + .ShowAsync(); + + List personalAccessTokens = await _userManagementService.GetPersonAccessTokens(CancellationToken.None); + PersonalAccessTokens = new ObservableCollection(personalAccessTokens); + } + + private async Task ExecuteDeleteToken(PersonalAccessToken token) + { + bool confirm = await _windowService.ShowConfirmContentDialog("Delete Personal Access Token", "Are you sure you want to delete this token? Any services using it will stop working"); + if (!confirm) + return; + + await _userManagementService.DeletePersonAccessToken(token, CancellationToken.None); + PersonalAccessTokens.Remove(token); + } + + private async Task LoadCurrentUser() { string? userId = _authenticationService.Claims.FirstOrDefault(c => c.Type == "sub")?.Value; Name = _authenticationService.Claims.FirstOrDefault(c => c.Type == "name")?.Value; Email = _authenticationService.Claims.FirstOrDefault(c => c.Type == "email")?.Value; AvatarUrl = $"{WorkshopConstants.AUTHORITY_URL}/user/avatar/{userId}"; + + List personalAccessTokens = await _userManagementService.GetPersonAccessTokens(CancellationToken.None); + PersonalAccessTokens = new ObservableCollection(personalAccessTokens); } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Sidebar/SidebarViewModel.cs b/src/Artemis.UI/Screens/Sidebar/SidebarViewModel.cs index bb2d56f95..3b7de1c0c 100644 --- a/src/Artemis.UI/Screens/Sidebar/SidebarViewModel.cs +++ b/src/Artemis.UI/Screens/Sidebar/SidebarViewModel.cs @@ -43,7 +43,7 @@ public partial class SidebarViewModel : ActivatableViewModelBase { new(MaterialIconKind.FolderVideo, "Profiles", "workshop/entries/profiles/1", "workshop/entries/profiles"), new(MaterialIconKind.KeyboardVariant, "Layouts", "workshop/entries/layouts/1", "workshop/entries/layouts"), - new(MaterialIconKind.Plugin, "Plugins", "workshop/entries/plugins/1", "workshop/entries/plugins"), + new(MaterialIconKind.Connection, "Plugins", "workshop/entries/plugins/1", "workshop/entries/plugins"), new(MaterialIconKind.Bookshelf, "Library", "workshop/library"), }), diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryReleasesViewModel.cs b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryReleasesViewModel.cs index 311164269..21be13b28 100644 --- a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryReleasesViewModel.cs +++ b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryReleasesViewModel.cs @@ -9,7 +9,6 @@ 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 Humanizer; using ReactiveUI; @@ -29,11 +28,13 @@ public class EntryReleasesViewModel : ViewModelBase Entry = entry; DownloadLatestRelease = ReactiveCommand.CreateFromTask(ExecuteDownloadLatestRelease); + OnInstallationStarted = Confirm; } public IGetEntryById_Entry Entry { get; } public ReactiveCommand DownloadLatestRelease { get; } + public Func> OnInstallationStarted { get; set; } public Func? OnInstallationFinished { get; set; } private async Task ExecuteDownloadLatestRelease(CancellationToken cancellationToken) @@ -41,11 +42,7 @@ public class EntryReleasesViewModel : ViewModelBase if (Entry.LatestRelease == null) return; - bool confirm = await _windowService.ShowConfirmContentDialog( - "Install latest release", - $"Are you sure you want to download and install version {Entry.LatestRelease.Version} of {Entry.Name}?" - ); - if (!confirm) + if (await OnInstallationStarted(Entry)) return; IEntryInstallationHandler installationHandler = _factory.CreateHandler(Entry.EntryType); @@ -64,4 +61,14 @@ public class EntryReleasesViewModel : ViewModelBase .WithSeverity(NotificationSeverity.Error).Show(); } } + + private async Task Confirm(IEntryDetails entryDetails) + { + bool confirm = await _windowService.ShowConfirmContentDialog( + "Install latest release", + $"Are you sure you want to download and install version {entryDetails.LatestRelease?.Version} of {entryDetails.Name}?" + ); + + return !confirm; + } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Home/WorkshopHomeView.axaml b/src/Artemis.UI/Screens/Workshop/Home/WorkshopHomeView.axaml index 4bc7bd2a4..c2ed7ebb8 100644 --- a/src/Artemis.UI/Screens/Workshop/Home/WorkshopHomeView.axaml +++ b/src/Artemis.UI/Screens/Workshop/Home/WorkshopHomeView.axaml @@ -59,7 +59,7 @@ + @@ -109,16 +109,26 @@ - + + + Tokens be used to communicate with Artemis APIs without using a username and password + + + + Description Created at Expires at + + + You have no active personal access tokens. + - + - @@ -130,8 +140,8 @@ diff --git a/src/Artemis.UI/Screens/Workshop/Library/Tabs/InstalledTabItemView.axaml b/src/Artemis.UI/Screens/Workshop/Library/Tabs/InstalledTabItemView.axaml index 00e7eae8f..4f5994982 100644 --- a/src/Artemis.UI/Screens/Workshop/Library/Tabs/InstalledTabItemView.axaml +++ b/src/Artemis.UI/Screens/Workshop/Library/Tabs/InstalledTabItemView.axaml @@ -20,7 +20,7 @@ HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" Command="{CompiledBinding ViewWorkshopPage}"> - + - + + Installed - + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + From 78d3b58f99f100be3a71a25cb753f8d3cb36b9f0 Mon Sep 17 00:00:00 2001 From: RobertBeekman Date: Tue, 20 Feb 2024 12:06:21 +0100 Subject: [PATCH 10/12] Upgrade .NET 8 and packages Include Qodana :D --- .github/workflows/qodana_code_quality.yml | 20 +++++++++++++ src/Artemis.Core/Artemis.Core.csproj | 2 +- src/Artemis.Storage/Artemis.Storage.csproj | 4 +-- src/Artemis.UI.Linux/Artemis.UI.Linux.csproj | 2 +- src/Artemis.UI.MacOS/Artemis.UI.MacOS.csproj | 2 +- .../Artemis.UI.Shared.csproj | 12 ++++---- .../Artemis.UI.Windows.csproj | 6 ++-- src/Artemis.UI/Artemis.UI.csproj | 6 ++-- .../Artemis.VisualScripting.csproj | 2 +- .../Artemis.WebClient.Updating.csproj | 6 ++-- .../Artemis.WebClient.Workshop.csproj | 6 ++-- src/qodana.yaml | 29 +++++++++++++++++++ 12 files changed, 73 insertions(+), 24 deletions(-) create mode 100644 .github/workflows/qodana_code_quality.yml create mode 100644 src/qodana.yaml diff --git a/.github/workflows/qodana_code_quality.yml b/.github/workflows/qodana_code_quality.yml new file mode 100644 index 000000000..482098d02 --- /dev/null +++ b/.github/workflows/qodana_code_quality.yml @@ -0,0 +1,20 @@ +name: Qodana +on: + workflow_dispatch: + pull_request: + push: + branches: + - master + - development + +jobs: + qodana: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: 'Qodana Scan' + uses: JetBrains/qodana-action@v2023.3 + env: + QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }} \ No newline at end of file diff --git a/src/Artemis.Core/Artemis.Core.csproj b/src/Artemis.Core/Artemis.Core.csproj index 86d6f2a97..6a96956a5 100644 --- a/src/Artemis.Core/Artemis.Core.csproj +++ b/src/Artemis.Core/Artemis.Core.csproj @@ -1,6 +1,6 @@ - net7.0 + net8.0 false false Artemis.Core diff --git a/src/Artemis.Storage/Artemis.Storage.csproj b/src/Artemis.Storage/Artemis.Storage.csproj index 679f1c3c7..d682d5570 100644 --- a/src/Artemis.Storage/Artemis.Storage.csproj +++ b/src/Artemis.Storage/Artemis.Storage.csproj @@ -1,12 +1,12 @@  - net7.0 + net8.0 false x64 - + \ No newline at end of file diff --git a/src/Artemis.UI.Linux/Artemis.UI.Linux.csproj b/src/Artemis.UI.Linux/Artemis.UI.Linux.csproj index c346e5027..c41b62a40 100644 --- a/src/Artemis.UI.Linux/Artemis.UI.Linux.csproj +++ b/src/Artemis.UI.Linux/Artemis.UI.Linux.csproj @@ -1,7 +1,7 @@ WinExe - net7.0 + net8.0 enable x64 x64 diff --git a/src/Artemis.UI.MacOS/Artemis.UI.MacOS.csproj b/src/Artemis.UI.MacOS/Artemis.UI.MacOS.csproj index b5cc49ac7..d762afde1 100644 --- a/src/Artemis.UI.MacOS/Artemis.UI.MacOS.csproj +++ b/src/Artemis.UI.MacOS/Artemis.UI.MacOS.csproj @@ -1,7 +1,7 @@  WinExe - net7.0 + net8.0 enable x64 x64 diff --git a/src/Artemis.UI.Shared/Artemis.UI.Shared.csproj b/src/Artemis.UI.Shared/Artemis.UI.Shared.csproj index 1be9809f0..67242b85a 100644 --- a/src/Artemis.UI.Shared/Artemis.UI.Shared.csproj +++ b/src/Artemis.UI.Shared/Artemis.UI.Shared.csproj @@ -1,7 +1,7 @@  Library - net7.0 + net8.0 enable bin\ x64 @@ -10,16 +10,16 @@ - + - - - + + + - + diff --git a/src/Artemis.UI.Windows/Artemis.UI.Windows.csproj b/src/Artemis.UI.Windows/Artemis.UI.Windows.csproj index b0d0e9593..b9abf03e3 100644 --- a/src/Artemis.UI.Windows/Artemis.UI.Windows.csproj +++ b/src/Artemis.UI.Windows/Artemis.UI.Windows.csproj @@ -1,7 +1,7 @@ WinExe - net7.0-windows10.0.17763.0 + net8.0-windows10.0.17763.0 enable x64 bin @@ -21,11 +21,11 @@ - + - + diff --git a/src/Artemis.UI/Artemis.UI.csproj b/src/Artemis.UI/Artemis.UI.csproj index ec42bd185..88b39600b 100644 --- a/src/Artemis.UI/Artemis.UI.csproj +++ b/src/Artemis.UI/Artemis.UI.csproj @@ -1,7 +1,7 @@ Library - net7.0 + net8.0 enable bin/ x64 @@ -19,11 +19,11 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Artemis.VisualScripting/Artemis.VisualScripting.csproj b/src/Artemis.VisualScripting/Artemis.VisualScripting.csproj index 1e5aea19c..90b14e4fa 100644 --- a/src/Artemis.VisualScripting/Artemis.VisualScripting.csproj +++ b/src/Artemis.VisualScripting/Artemis.VisualScripting.csproj @@ -1,6 +1,6 @@  - net7.0 + net8.0 enable enable x64 diff --git a/src/Artemis.WebClient.Updating/Artemis.WebClient.Updating.csproj b/src/Artemis.WebClient.Updating/Artemis.WebClient.Updating.csproj index 732b2c721..23da69ca1 100644 --- a/src/Artemis.WebClient.Updating/Artemis.WebClient.Updating.csproj +++ b/src/Artemis.WebClient.Updating/Artemis.WebClient.Updating.csproj @@ -1,6 +1,6 @@ - net7.0 + net8.0 enable enable x64 @@ -9,8 +9,8 @@ - - + + diff --git a/src/Artemis.WebClient.Workshop/Artemis.WebClient.Workshop.csproj b/src/Artemis.WebClient.Workshop/Artemis.WebClient.Workshop.csproj index b9931c626..840e6d736 100644 --- a/src/Artemis.WebClient.Workshop/Artemis.WebClient.Workshop.csproj +++ b/src/Artemis.WebClient.Workshop/Artemis.WebClient.Workshop.csproj @@ -1,6 +1,6 @@ - net7.0 + net8.0 enable enable x64 @@ -10,8 +10,8 @@ - - + + diff --git a/src/qodana.yaml b/src/qodana.yaml new file mode 100644 index 000000000..f2bd5156e --- /dev/null +++ b/src/qodana.yaml @@ -0,0 +1,29 @@ +#-------------------------------------------------------------------------------# +# Qodana analysis is configured by qodana.yaml file # +# https://www.jetbrains.com/help/qodana/qodana-yaml.html # +#-------------------------------------------------------------------------------# +version: "1.0" + +#Specify IDE code to run analysis without container (Applied in CI/CD pipeline) +ide: QDNET + +#Specify inspection profile for code analysis +profile: + name: qodana.starter + +#Enable inspections +#include: +# - name: + +#Disable inspections +#exclude: +# - name: +# paths: +# - + +#Execute shell command before Qodana execution (Applied in CI/CD pipeline) +#bootstrap: sh ./prepare-qodana.sh + +#Install IDE plugins before Qodana execution (Applied in CI/CD pipeline) +#plugins: +# - id: #(plugin id can be found at https://plugins.jetbrains.com) From 1ed13d8b68b9d573dea0f2b5e3d700363a18ed0a Mon Sep 17 00:00:00 2001 From: RobertBeekman Date: Tue, 20 Feb 2024 12:19:29 +0100 Subject: [PATCH 11/12] Remove Qodana for now --- .github/workflows/qodana_code_quality.yml | 20 ---------------- src/qodana.yaml | 29 ----------------------- 2 files changed, 49 deletions(-) delete mode 100644 .github/workflows/qodana_code_quality.yml delete mode 100644 src/qodana.yaml diff --git a/.github/workflows/qodana_code_quality.yml b/.github/workflows/qodana_code_quality.yml deleted file mode 100644 index 482098d02..000000000 --- a/.github/workflows/qodana_code_quality.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Qodana -on: - workflow_dispatch: - pull_request: - push: - branches: - - master - - development - -jobs: - qodana: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - name: 'Qodana Scan' - uses: JetBrains/qodana-action@v2023.3 - env: - QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }} \ No newline at end of file diff --git a/src/qodana.yaml b/src/qodana.yaml deleted file mode 100644 index f2bd5156e..000000000 --- a/src/qodana.yaml +++ /dev/null @@ -1,29 +0,0 @@ -#-------------------------------------------------------------------------------# -# Qodana analysis is configured by qodana.yaml file # -# https://www.jetbrains.com/help/qodana/qodana-yaml.html # -#-------------------------------------------------------------------------------# -version: "1.0" - -#Specify IDE code to run analysis without container (Applied in CI/CD pipeline) -ide: QDNET - -#Specify inspection profile for code analysis -profile: - name: qodana.starter - -#Enable inspections -#include: -# - name: - -#Disable inspections -#exclude: -# - name: -# paths: -# - - -#Execute shell command before Qodana execution (Applied in CI/CD pipeline) -#bootstrap: sh ./prepare-qodana.sh - -#Install IDE plugins before Qodana execution (Applied in CI/CD pipeline) -#plugins: -# - id: #(plugin id can be found at https://plugins.jetbrains.com) From f51aa3ca06bfb248ca8f410e8e102b3f12739688 Mon Sep 17 00:00:00 2001 From: RobertBeekman Date: Tue, 20 Feb 2024 12:58:24 +0100 Subject: [PATCH 12/12] Bump RGB.NET --- src/Artemis.Core/Artemis.Core.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Artemis.Core/Artemis.Core.csproj b/src/Artemis.Core/Artemis.Core.csproj index 6a96956a5..8581e4835 100644 --- a/src/Artemis.Core/Artemis.Core.csproj +++ b/src/Artemis.Core/Artemis.Core.csproj @@ -43,9 +43,9 @@ - - - + + +