From 9e994840f6e3d98893c002b1d3fd44a23b4e10d4 Mon Sep 17 00:00:00 2001 From: RobertBeekman Date: Wed, 27 Mar 2024 21:43:45 +0100 Subject: [PATCH] Workshop - Added dedicated release page --- .../Routing/Route/RouteRegistration.cs | 11 +++ src/Artemis.UI/Artemis.UI.csproj | 4 + src/Artemis.UI/Routing/Routes.cs | 98 ++++++++----------- .../Updating/ReleaseDetailsView.axaml | 1 - .../EntryReleases/EntryReleaseView.axaml | 92 +++++++++++++++++ .../EntryReleases/EntryReleaseView.axaml.cs | 14 +++ .../EntryReleases/EntryReleaseViewModel.cs | 30 ++++++ .../EntryReleasesView.axaml | 15 +-- .../EntryReleasesView.axaml.cs | 0 .../EntryReleasesViewModel.cs | 15 ++- .../Layout/LayoutDescriptionView.axaml | 19 ++++ .../Layout/LayoutDescriptionView.axaml.cs | 14 +++ .../Layout/LayoutDescriptionViewModel.cs | 10 ++ .../Workshop/Layout/LayoutDetailsView.axaml | 16 ++- .../Layout/LayoutDetailsView.axaml.cs | 6 ++ .../Workshop/Layout/LayoutDetailsViewModel.cs | 11 ++- .../Workshop/Layout/LayoutListView.axaml.cs | 12 +-- .../Parameters/ReleaseDetailParameters.cs | 6 ++ .../Plugins/PluginDescriptionView.axaml | 35 +++++++ .../Plugins/PluginDescriptionView.axaml.cs | 14 +++ .../Plugins/PluginDescriptionViewModel.cs | 41 ++++++++ .../Workshop/Plugins/PluginDetailsView.axaml | 32 ++---- .../Plugins/PluginDetailsView.axaml.cs | 6 ++ .../Plugins/PluginDetailsViewModel.cs | 22 ++--- .../Workshop/Plugins/PluginListView.axaml.cs | 13 +-- .../Profile/ProfileDescriptionView.axaml | 35 +++++++ .../Profile/ProfileDescriptionView.axaml.cs | 14 +++ .../Profile/ProfileDescriptionViewModel.cs | 41 ++++++++ .../Workshop/Profile/ProfileDetailsView.axaml | 33 ++----- .../Profile/ProfileDetailsView.axaml.cs | 6 ++ .../Profile/ProfileDetailsViewModel.cs | 24 +++-- .../Workshop/Profile/ProfileListView.axaml.cs | 14 +-- .../Artemis.WebClient.Workshop.csproj | 3 + .../Queries/GetReleaseById.graphql | 6 ++ .../graphql.config.yml | 2 +- src/Artemis.WebClient.Workshop/schema.graphql | 10 ++ 36 files changed, 535 insertions(+), 190 deletions(-) create mode 100644 src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseView.axaml create mode 100644 src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseView.axaml.cs create mode 100644 src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseViewModel.cs rename src/Artemis.UI/Screens/Workshop/{Entries/Details => EntryReleases}/EntryReleasesView.axaml (87%) rename src/Artemis.UI/Screens/Workshop/{Entries/Details => EntryReleases}/EntryReleasesView.axaml.cs (100%) rename src/Artemis.UI/Screens/Workshop/{Entries/Details => EntryReleases}/EntryReleasesViewModel.cs (84%) create mode 100644 src/Artemis.UI/Screens/Workshop/Layout/LayoutDescriptionView.axaml create mode 100644 src/Artemis.UI/Screens/Workshop/Layout/LayoutDescriptionView.axaml.cs create mode 100644 src/Artemis.UI/Screens/Workshop/Layout/LayoutDescriptionViewModel.cs create mode 100644 src/Artemis.UI/Screens/Workshop/Parameters/ReleaseDetailParameters.cs create mode 100644 src/Artemis.UI/Screens/Workshop/Plugins/PluginDescriptionView.axaml create mode 100644 src/Artemis.UI/Screens/Workshop/Plugins/PluginDescriptionView.axaml.cs create mode 100644 src/Artemis.UI/Screens/Workshop/Plugins/PluginDescriptionViewModel.cs create mode 100644 src/Artemis.UI/Screens/Workshop/Profile/ProfileDescriptionView.axaml create mode 100644 src/Artemis.UI/Screens/Workshop/Profile/ProfileDescriptionView.axaml.cs create mode 100644 src/Artemis.UI/Screens/Workshop/Profile/ProfileDescriptionViewModel.cs create mode 100644 src/Artemis.WebClient.Workshop/Queries/GetReleaseById.graphql diff --git a/src/Artemis.UI.Shared/Routing/Route/RouteRegistration.cs b/src/Artemis.UI.Shared/Routing/Route/RouteRegistration.cs index c62437d03..22246b3f0 100644 --- a/src/Artemis.UI.Shared/Routing/Route/RouteRegistration.cs +++ b/src/Artemis.UI.Shared/Routing/Route/RouteRegistration.cs @@ -18,6 +18,17 @@ public class RouteRegistration : IRouterRegistration where TViewMode Route = new Route(path); } + /// + /// Initializes a new instance of the class. + /// + /// The path of the route. + /// The children of the route. + public RouteRegistration(string path, List children) + { + Route = new Route(path); + Children = children; + } + /// public override string ToString() { diff --git a/src/Artemis.UI/Artemis.UI.csproj b/src/Artemis.UI/Artemis.UI.csproj index 7408b305b..c52465960 100644 --- a/src/Artemis.UI/Artemis.UI.csproj +++ b/src/Artemis.UI/Artemis.UI.csproj @@ -56,6 +56,10 @@ LayoutListView.axaml Code + + EntryReleasesView.axaml + Code + diff --git a/src/Artemis.UI/Routing/Routes.cs b/src/Artemis.UI/Routing/Routes.cs index 09a677350..e64c17868 100644 --- a/src/Artemis.UI/Routing/Routes.cs +++ b/src/Artemis.UI/Routing/Routes.cs @@ -7,6 +7,7 @@ using Artemis.UI.Screens.Settings.Updating; using Artemis.UI.Screens.SurfaceEditor; using Artemis.UI.Screens.Workshop; using Artemis.UI.Screens.Workshop.Entries; +using Artemis.UI.Screens.Workshop.EntryReleases; using Artemis.UI.Screens.Workshop.Home; using Artemis.UI.Screens.Workshop.Layout; using Artemis.UI.Screens.Workshop.Library; @@ -16,68 +17,51 @@ using Artemis.UI.Screens.Workshop.Profile; using Artemis.UI.Shared.Routing; using PluginDetailsViewModel = Artemis.UI.Screens.Workshop.Plugins.PluginDetailsViewModel; -namespace Artemis.UI.Routing; - -public static class Routes +namespace Artemis.UI.Routing { - public static List ArtemisRoutes = - [ - new RouteRegistration("blank"), - new RouteRegistration("home"), - new RouteRegistration("workshop") - { - Children = - [ + public static class Routes + { + public static readonly List ArtemisRoutes = + [ + new RouteRegistration("blank"), + new RouteRegistration("home"), + new RouteRegistration("workshop", [ new RouteRegistration("offline/{message:string}"), - new RouteRegistration("entries") - { - Children = - [ - new RouteRegistration("plugins") - { - Children = [new RouteRegistration("details/{entryId:long}")] - }, - new RouteRegistration("profiles") - { - Children = [new RouteRegistration("details/{entryId:long}")] - }, - new RouteRegistration("layouts") - { - Children = [new RouteRegistration("details/{entryId:long}")] - }, - ] - }, - - new RouteRegistration("library") - { - Children = - [ - new RouteRegistration("installed"), - new RouteRegistration("submissions"), - new RouteRegistration("submissions/{entryId:long}") - ] - } - ] - }, - - new RouteRegistration("surface-editor"), - new RouteRegistration("settings") - { - Children = - [ + new RouteRegistration("entries", [ + new RouteRegistration("plugins", [ + new RouteRegistration("details/{entryId:long}", [ + new RouteRegistration("releases/{releaseId:long}") + ]) + ]), + new RouteRegistration("profiles", [ + new RouteRegistration("details/{entryId:long}", [ + new RouteRegistration("releases/{releaseId:long}") + ]) + ]), + new RouteRegistration("layouts", [ + new RouteRegistration("details/{entryId:long}", [ + new RouteRegistration("releases/{releaseId:long}") + ]) + ]) + ]), + new RouteRegistration("library", [ + new RouteRegistration("installed"), + new RouteRegistration("submissions"), + new RouteRegistration("submissions/{entryId:long}") + ]) + ]), + new RouteRegistration("surface-editor"), + new RouteRegistration("settings", [ new RouteRegistration("general"), new RouteRegistration("plugins"), new RouteRegistration("devices"), - new RouteRegistration("releases") - { - Children = [new RouteRegistration("{releaseId:guid}")] - }, - + new RouteRegistration("releases", [ + new RouteRegistration("{releaseId:guid}") + ]), new RouteRegistration("account"), new RouteRegistration("about") - ] - }, - - new RouteRegistration("profile-editor/{profileConfigurationId:guid}") - ]; + ]), + new RouteRegistration("profile-editor/{profileConfigurationId:guid}") + ]; + } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Settings/Updating/ReleaseDetailsView.axaml b/src/Artemis.UI/Screens/Settings/Updating/ReleaseDetailsView.axaml index ef4b22704..34258aae0 100644 --- a/src/Artemis.UI/Screens/Settings/Updating/ReleaseDetailsView.axaml +++ b/src/Artemis.UI/Screens/Settings/Updating/ReleaseDetailsView.axaml @@ -149,5 +149,4 @@ - \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseView.axaml b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseView.axaml new file mode 100644 index 000000000..4a44dbaa1 --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseView.axaml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + Release info + + + + + Release date + + + + + + Version + + + + + + File size + + + + + + + + + + Release notes + + + + + + + + + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseView.axaml.cs b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseView.axaml.cs new file mode 100644 index 000000000..ea7532c70 --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseView.axaml.cs @@ -0,0 +1,14 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using Avalonia.ReactiveUI; + +namespace Artemis.UI.Screens.Workshop.EntryReleases; + +public partial class EntryReleaseView : ReactiveUserControl +{ + public EntryReleaseView() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseViewModel.cs b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseViewModel.cs new file mode 100644 index 000000000..d92912c5c --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseViewModel.cs @@ -0,0 +1,30 @@ +using System.Threading; +using System.Threading.Tasks; +using Artemis.UI.Screens.Workshop.Parameters; +using Artemis.UI.Shared.Routing; +using Artemis.WebClient.Workshop; +using PropertyChanged.SourceGenerator; +using StrawberryShake; + +namespace Artemis.UI.Screens.Workshop.EntryReleases; + +public partial class EntryReleaseViewModel : RoutableScreen +{ + private readonly IWorkshopClient _client; + [Notify] private IGetReleaseById_Release? _release; + + public EntryReleaseViewModel(IWorkshopClient client) + { + _client = client; + } + + /// + public override async Task OnNavigating(ReleaseDetailParameters parameters, NavigationArguments args, CancellationToken cancellationToken) + { + IOperationResult result = await _client.GetReleaseById.ExecuteAsync(parameters.ReleaseId, cancellationToken); + if (result.IsErrorResult()) + return; + + Release = result.Data?.Release; + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryReleasesView.axaml b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleasesView.axaml similarity index 87% rename from src/Artemis.UI/Screens/Workshop/Entries/Details/EntryReleasesView.axaml rename to src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleasesView.axaml index 5453ac531..899065ec7 100644 --- a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryReleasesView.axaml +++ b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleasesView.axaml @@ -25,12 +25,7 @@ CommandParameter="{CompiledBinding LatestRelease}"> - - + @@ -62,12 +57,8 @@ Command="{Binding $parent[details:EntryReleasesView].DataContext.NavigateToRelease}" CommandParameter="{CompiledBinding}"> - - + + diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryReleasesView.axaml.cs b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleasesView.axaml.cs similarity index 100% rename from src/Artemis.UI/Screens/Workshop/Entries/Details/EntryReleasesView.axaml.cs rename to src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleasesView.axaml.cs diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryReleasesViewModel.cs b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleasesViewModel.cs similarity index 84% rename from src/Artemis.UI/Screens/Workshop/Entries/Details/EntryReleasesViewModel.cs rename to src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleasesViewModel.cs index 8d35a9f38..4ec497e37 100644 --- a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryReleasesViewModel.cs +++ b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleasesViewModel.cs @@ -52,7 +52,20 @@ public class EntryReleasesViewModel : ViewModelBase private async Task ExecuteNavigateToRelease(IRelease release) { - await _router.Navigate($"workshop/entries/{Entry.Id}/releases/{release.Id}"); + switch (Entry.EntryType) + { + case EntryType.Profile: + await _router.Navigate($"workshop/entries/profiles/details/{Entry.Id}/releases/{release.Id}"); + break; + case EntryType.Layout: + await _router.Navigate($"workshop/entries/layouts/details/{Entry.Id}/releases/{release.Id}"); + break; + case EntryType.Plugin: + await _router.Navigate($"workshop/entries/plugins/details/{Entry.Id}/releases/{release.Id}"); + break; + default: + throw new ArgumentOutOfRangeException(nameof(Entry.EntryType)); + } } private async Task ExecuteDownloadLatestRelease(CancellationToken cancellationToken) diff --git a/src/Artemis.UI/Screens/Workshop/Layout/LayoutDescriptionView.axaml b/src/Artemis.UI/Screens/Workshop/Layout/LayoutDescriptionView.axaml new file mode 100644 index 000000000..e4cf60508 --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Layout/LayoutDescriptionView.axaml @@ -0,0 +1,19 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Layout/LayoutDescriptionView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Layout/LayoutDescriptionView.axaml.cs new file mode 100644 index 000000000..5276e422f --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Layout/LayoutDescriptionView.axaml.cs @@ -0,0 +1,14 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using Avalonia.ReactiveUI; + +namespace Artemis.UI.Screens.Workshop.Layout; + +public partial class LayoutDescriptionView : ReactiveUserControl +{ + public LayoutDescriptionView() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Layout/LayoutDescriptionViewModel.cs b/src/Artemis.UI/Screens/Workshop/Layout/LayoutDescriptionViewModel.cs new file mode 100644 index 000000000..3fb8f678e --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Layout/LayoutDescriptionViewModel.cs @@ -0,0 +1,10 @@ +using Artemis.UI.Shared.Routing; +using Artemis.WebClient.Workshop; +using PropertyChanged.SourceGenerator; + +namespace Artemis.UI.Screens.Workshop.Layout; + +public partial class LayoutDescriptionViewModel : RoutableScreen +{ + [Notify] private IEntryDetails? _entry; +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Layout/LayoutDetailsView.axaml b/src/Artemis.UI/Screens/Workshop/Layout/LayoutDetailsView.axaml index c8ed98401..6370121be 100644 --- a/src/Artemis.UI/Screens/Workshop/Layout/LayoutDetailsView.axaml +++ b/src/Artemis.UI/Screens/Workshop/Layout/LayoutDetailsView.axaml @@ -4,6 +4,8 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:layout="clr-namespace:Artemis.UI.Screens.Workshop.Layout" xmlns:mdxaml="https://github.com/whistyun/Markdown.Avalonia.Tight" + xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" + xmlns:ui="clr-namespace:Artemis.UI" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="800" x:Class="Artemis.UI.Screens.Workshop.Layout.LayoutDetailsView" x:DataType="layout:LayoutDetailsViewModel"> @@ -18,15 +20,11 @@ - - - - - - - - - + + + + + diff --git a/src/Artemis.UI/Screens/Workshop/Layout/LayoutDetailsView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Layout/LayoutDetailsView.axaml.cs index 57e93b1e1..d8e9ff858 100644 --- a/src/Artemis.UI/Screens/Workshop/Layout/LayoutDetailsView.axaml.cs +++ b/src/Artemis.UI/Screens/Workshop/Layout/LayoutDetailsView.axaml.cs @@ -1,4 +1,7 @@ +using System; +using System.Reactive.Disposables; using Avalonia.ReactiveUI; +using ReactiveUI; namespace Artemis.UI.Screens.Workshop.Layout; @@ -7,5 +10,8 @@ public partial class LayoutDetailsView : ReactiveUserControl ViewModel.WhenAnyValue(vm => vm.Screen) + .Subscribe(screen => RouterFrame.NavigateFromObject(screen ?? ViewModel?.LayoutDescriptionViewModel)) + .DisposeWith(d)); } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Layout/LayoutDetailsViewModel.cs b/src/Artemis.UI/Screens/Workshop/Layout/LayoutDetailsViewModel.cs index 9663f5ad4..69853e0f9 100644 --- a/src/Artemis.UI/Screens/Workshop/Layout/LayoutDetailsViewModel.cs +++ b/src/Artemis.UI/Screens/Workshop/Layout/LayoutDetailsViewModel.cs @@ -19,7 +19,7 @@ using StrawberryShake; namespace Artemis.UI.Screens.Workshop.Layout; -public partial class LayoutDetailsViewModel : RoutableScreen +public partial class LayoutDetailsViewModel : RoutableHostScreen { private readonly IWorkshopClient _client; private readonly IDeviceService _deviceService; @@ -35,10 +35,12 @@ public partial class LayoutDetailsViewModel : RoutableScreen getEntryInfoViewModel, Func getEntryReleasesViewModel, Func getEntryImagesViewModel) { + LayoutDescriptionViewModel = layoutDescriptionViewModel; _client = client; _deviceService = deviceService; _windowService = windowService; @@ -47,9 +49,12 @@ public partial class LayoutDetailsViewModel : RoutableScreen public LayoutListView() { InitializeComponent(); - this.WhenActivated(d => - { - ViewModel.WhenAnyValue(vm => vm.Screen).Subscribe(Navigate).DisposeWith(d); - }); - } - - private void Navigate(RoutableScreen? viewModel) - { - RouterFrame.NavigateFromObject(viewModel ?? ViewModel?.EntryListViewModel); + this.WhenActivated(d => ViewModel.WhenAnyValue(vm => vm.Screen) + .Subscribe(screen => RouterFrame.NavigateFromObject(screen ?? ViewModel?.EntryListViewModel)) + .DisposeWith(d)); } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Parameters/ReleaseDetailParameters.cs b/src/Artemis.UI/Screens/Workshop/Parameters/ReleaseDetailParameters.cs new file mode 100644 index 000000000..252095dad --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Parameters/ReleaseDetailParameters.cs @@ -0,0 +1,6 @@ +namespace Artemis.UI.Screens.Workshop.Parameters; + +public class ReleaseDetailParameters +{ + public long ReleaseId { get; set; } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Plugins/PluginDescriptionView.axaml b/src/Artemis.UI/Screens/Workshop/Plugins/PluginDescriptionView.axaml new file mode 100644 index 000000000..24c91abf8 --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Plugins/PluginDescriptionView.axaml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + Used by these profiles + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Plugins/PluginDescriptionView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Plugins/PluginDescriptionView.axaml.cs new file mode 100644 index 000000000..bffef29b2 --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Plugins/PluginDescriptionView.axaml.cs @@ -0,0 +1,14 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using Avalonia.ReactiveUI; + +namespace Artemis.UI.Screens.Workshop.Plugins; + +public partial class PluginDescriptionView : ReactiveUserControl +{ + public PluginDescriptionView() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Plugins/PluginDescriptionViewModel.cs b/src/Artemis.UI/Screens/Workshop/Plugins/PluginDescriptionViewModel.cs new file mode 100644 index 000000000..0cae2bbcd --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Plugins/PluginDescriptionViewModel.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Artemis.UI.Screens.Workshop.Entries.List; +using Artemis.UI.Shared.Routing; +using Artemis.WebClient.Workshop; +using PropertyChanged.SourceGenerator; + +namespace Artemis.UI.Screens.Workshop.Plugins; + +public partial class PluginDescriptionViewModel : RoutableScreen +{ + [Notify] private IEntryDetails? _entry; + [Notify] private List? _dependants; + private readonly IWorkshopClient _client; + private readonly Func _getEntryListViewModel; + + public PluginDescriptionViewModel(IWorkshopClient client, Func getEntryListViewModel) + { + _client = client; + _getEntryListViewModel = getEntryListViewModel; + } + + public async Task SetEntry(IEntryDetails? entry, CancellationToken cancellationToken) + { + Entry = entry; + + if (entry != null) + { + IReadOnlyList? dependants = (await _client.GetDependantEntries.ExecuteAsync(entry.Id, 0, 25, cancellationToken)).Data?.Entries?.Items; + Dependants = dependants != null && dependants.Any() ? dependants.Select(_getEntryListViewModel).OrderByDescending(d => d.Entry.Downloads).Take(10).ToList() : null; + } + else + { + Dependants = null; + } + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Plugins/PluginDetailsView.axaml b/src/Artemis.UI/Screens/Workshop/Plugins/PluginDetailsView.axaml index 1aa45690d..565898da6 100644 --- a/src/Artemis.UI/Screens/Workshop/Plugins/PluginDetailsView.axaml +++ b/src/Artemis.UI/Screens/Workshop/Plugins/PluginDetailsView.axaml @@ -5,6 +5,8 @@ xmlns:mdxaml="https://github.com/whistyun/Markdown.Avalonia.Tight" xmlns:plugins="clr-namespace:Artemis.UI.Screens.Workshop.Plugins" xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" + xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" + xmlns:ui="clr-namespace:Artemis.UI" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="Artemis.UI.Screens.Workshop.Plugins.PluginDetailsView" x:DataType="plugins:PluginDetailsViewModel"> @@ -35,31 +37,11 @@ - - - - - - - - - - - - Used by these profiles - - - - - - - - - - - - - + + + + + diff --git a/src/Artemis.UI/Screens/Workshop/Plugins/PluginDetailsView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Plugins/PluginDetailsView.axaml.cs index c50ffbe89..d4461a4b9 100644 --- a/src/Artemis.UI/Screens/Workshop/Plugins/PluginDetailsView.axaml.cs +++ b/src/Artemis.UI/Screens/Workshop/Plugins/PluginDetailsView.axaml.cs @@ -1,4 +1,7 @@ +using System; +using System.Reactive.Disposables; using Avalonia.ReactiveUI; +using ReactiveUI; namespace Artemis.UI.Screens.Workshop.Plugins; @@ -7,5 +10,8 @@ public partial class PluginDetailsView : ReactiveUserControl ViewModel.WhenAnyValue(vm => vm.Screen) + .Subscribe(screen => RouterFrame.NavigateFromObject(screen ?? ViewModel?.PluginDescriptionViewModel)) + .DisposeWith(d)); } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Plugins/PluginDetailsViewModel.cs b/src/Artemis.UI/Screens/Workshop/Plugins/PluginDetailsViewModel.cs index 9a2d94314..b2c222fbb 100644 --- a/src/Artemis.UI/Screens/Workshop/Plugins/PluginDetailsViewModel.cs +++ b/src/Artemis.UI/Screens/Workshop/Plugins/PluginDetailsViewModel.cs @@ -19,7 +19,7 @@ using StrawberryShake; namespace Artemis.UI.Screens.Workshop.Plugins; -public partial class PluginDetailsViewModel : RoutableScreen +public partial class PluginDetailsViewModel : RoutableHostScreen { private readonly IWorkshopClient _client; private readonly IWindowService _windowService; @@ -27,7 +27,6 @@ public partial class PluginDetailsViewModel : RoutableScreen _getEntryInfoViewModel; private readonly Func _getEntryReleasesViewModel; private readonly Func _getEntryImagesViewModel; - private readonly Func _getEntryListViewModel; [Notify] private IGetPluginEntryById_Entry? _entry; [Notify] private EntryInfoViewModel? _entryInfoViewModel; [Notify] private EntryReleasesViewModel? _entryReleasesViewModel; @@ -37,23 +36,26 @@ public partial class PluginDetailsViewModel : RoutableScreen getEntryInfoViewModel, Func getEntryReleasesViewModel, - Func getEntryImagesViewModel, - Func getEntryListViewModel) + Func getEntryImagesViewModel) { + PluginDescriptionViewModel = pluginDescriptionViewModel; _client = client; _windowService = windowService; _pluginManagementService = pluginManagementService; _getEntryInfoViewModel = getEntryInfoViewModel; _getEntryReleasesViewModel = getEntryReleasesViewModel; _getEntryImagesViewModel = getEntryImagesViewModel; - _getEntryListViewModel = getEntryListViewModel; } + public PluginDescriptionViewModel PluginDescriptionViewModel { get; } + public override async Task OnNavigating(WorkshopDetailParameters parameters, NavigationArguments args, CancellationToken cancellationToken) { - await GetEntry(parameters.EntryId, cancellationToken); + if (Entry?.Id != parameters.EntryId) + await GetEntry(parameters.EntryId, cancellationToken); } private async Task GetEntry(long entryId, CancellationToken cancellationToken) @@ -73,13 +75,7 @@ public partial class PluginDetailsViewModel : RoutableScreen? dependants = (await _client.GetDependantEntries.ExecuteAsync(entryId, 0, 25, cancellationToken)).Data?.Entries?.Items; - Dependants = dependants != null && dependants.Any() - ? new ReadOnlyObservableCollection(new ObservableCollection(dependants - .Select(_getEntryListViewModel) - .OrderByDescending(d => d.Entry.Downloads) - .Take(10))) - : null; + await PluginDescriptionViewModel.SetEntry(Entry, cancellationToken); } private async Task OnInstallationStarted(IEntryDetails entryDetails, IRelease release) diff --git a/src/Artemis.UI/Screens/Workshop/Plugins/PluginListView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Plugins/PluginListView.axaml.cs index 27b52c560..ac248f205 100644 --- a/src/Artemis.UI/Screens/Workshop/Plugins/PluginListView.axaml.cs +++ b/src/Artemis.UI/Screens/Workshop/Plugins/PluginListView.axaml.cs @@ -1,6 +1,5 @@ using System; using System.Reactive.Disposables; -using Artemis.UI.Shared.Routing; using Avalonia.ReactiveUI; using ReactiveUI; @@ -11,14 +10,8 @@ public partial class PluginListView : ReactiveUserControl public PluginListView() { InitializeComponent(); - this.WhenActivated(d => - { - ViewModel.WhenAnyValue(vm => vm.Screen).Subscribe(Navigate).DisposeWith(d); - }); - } - - private void Navigate(RoutableScreen? viewModel) - { - RouterFrame.NavigateFromObject(viewModel ?? ViewModel?.EntryListViewModel); + this.WhenActivated(d => ViewModel.WhenAnyValue(vm => vm.Screen) + .Subscribe(screen => RouterFrame.NavigateFromObject(screen ?? ViewModel?.EntryListViewModel)) + .DisposeWith(d)); } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Profile/ProfileDescriptionView.axaml b/src/Artemis.UI/Screens/Workshop/Profile/ProfileDescriptionView.axaml new file mode 100644 index 000000000..ff076cdb6 --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Profile/ProfileDescriptionView.axaml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + Required plugins + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Profile/ProfileDescriptionView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Profile/ProfileDescriptionView.axaml.cs new file mode 100644 index 000000000..5fed996a6 --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Profile/ProfileDescriptionView.axaml.cs @@ -0,0 +1,14 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using Avalonia.ReactiveUI; + +namespace Artemis.UI.Screens.Workshop.Profile; + +public partial class ProfileDescriptionView : ReactiveUserControl +{ + public ProfileDescriptionView() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Profile/ProfileDescriptionViewModel.cs b/src/Artemis.UI/Screens/Workshop/Profile/ProfileDescriptionViewModel.cs new file mode 100644 index 000000000..fd16c6cac --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Profile/ProfileDescriptionViewModel.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Artemis.UI.Screens.Workshop.Entries.List; +using Artemis.UI.Shared.Routing; +using Artemis.WebClient.Workshop; +using PropertyChanged.SourceGenerator; + +namespace Artemis.UI.Screens.Workshop.Profile; + +public partial class ProfileDescriptionViewModel : RoutableScreen +{ + private readonly IWorkshopClient _client; + private readonly Func _getEntryListViewModel; + [Notify] private IEntryDetails? _entry; + [Notify] private List? _dependencies; + + public ProfileDescriptionViewModel(IWorkshopClient client, Func getEntryListViewModel) + { + _client = client; + _getEntryListViewModel = getEntryListViewModel; + } + + public async Task SetEntry(IEntryDetails? entry, CancellationToken cancellationToken) + { + Entry = entry; + + if (entry != null) + { + IReadOnlyList? dependencies = (await _client.GetLatestDependencies.ExecuteAsync(entry.Id, cancellationToken)).Data?.Entry?.LatestRelease?.Dependencies; + Dependencies = dependencies != null && dependencies.Any() ? dependencies.Select(_getEntryListViewModel).ToList() : null; + } + else + { + Dependencies = null; + } + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsView.axaml b/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsView.axaml index 9220a2b49..9e5c41be6 100644 --- a/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsView.axaml +++ b/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsView.axaml @@ -4,6 +4,8 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:profile="clr-namespace:Artemis.UI.Screens.Workshop.Profile" xmlns:mdxaml="https://github.com/whistyun/Markdown.Avalonia.Tight" + xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" + xmlns:ui="clr-namespace:Artemis.UI" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="800" x:Class="Artemis.UI.Screens.Workshop.Profile.ProfileDetailsView" x:DataType="profile:ProfileDetailsViewModel"> @@ -18,33 +20,12 @@ - - - - - - - - - - - - Required plugins - - - - - - - - - - - - - + + + + + - diff --git a/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsView.axaml.cs index 1150bd94c..7a1898ac4 100644 --- a/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsView.axaml.cs +++ b/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsView.axaml.cs @@ -1,4 +1,7 @@ +using System; +using System.Reactive.Disposables; using Avalonia.ReactiveUI; +using ReactiveUI; namespace Artemis.UI.Screens.Workshop.Profile; @@ -7,5 +10,8 @@ public partial class ProfileDetailsView : ReactiveUserControl ViewModel.WhenAnyValue(vm => vm.Screen) + .Subscribe(screen => RouterFrame.NavigateFromObject(screen ?? ViewModel?.ProfileDescriptionViewModel)) + .DisposeWith(d)); } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsViewModel.cs b/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsViewModel.cs index 8aadc53d4..1c26abb8d 100644 --- a/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsViewModel.cs +++ b/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsViewModel.cs @@ -5,7 +5,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using Artemis.UI.Screens.Workshop.Entries.Details; -using Artemis.UI.Screens.Workshop.Entries.List; using Artemis.UI.Screens.Workshop.Parameters; using Artemis.UI.Shared.Routing; using Artemis.WebClient.Workshop; @@ -14,36 +13,38 @@ using StrawberryShake; namespace Artemis.UI.Screens.Workshop.Profile; -public partial class ProfileDetailsViewModel : RoutableScreen +public partial class ProfileDetailsViewModel : RoutableHostScreen { private readonly IWorkshopClient _client; private readonly Func _getEntryInfoViewModel; private readonly Func _getEntryReleasesViewModel; private readonly Func _getEntryImagesViewModel; - private readonly Func _getEntryListViewModel; [Notify] private IEntryDetails? _entry; [Notify] private EntryInfoViewModel? _entryInfoViewModel; [Notify] private EntryReleasesViewModel? _entryReleasesViewModel; [Notify] private EntryImagesViewModel? _entryImagesViewModel; - [Notify] private ReadOnlyObservableCollection? _dependencies; public ProfileDetailsViewModel(IWorkshopClient client, + ProfileDescriptionViewModel profileDescriptionViewModel, Func getEntryInfoViewModel, Func getEntryReleasesViewModel, - Func getEntryImagesViewModel, - Func getEntryListViewModel) + Func getEntryImagesViewModel) { + ProfileDescriptionViewModel = profileDescriptionViewModel; + _client = client; _getEntryInfoViewModel = getEntryInfoViewModel; _getEntryReleasesViewModel = getEntryReleasesViewModel; _getEntryImagesViewModel = getEntryImagesViewModel; - _getEntryListViewModel = getEntryListViewModel; } + public ProfileDescriptionViewModel ProfileDescriptionViewModel { get; } + public override async Task OnNavigating(WorkshopDetailParameters parameters, NavigationArguments args, CancellationToken cancellationToken) { - await GetEntry(parameters.EntryId, cancellationToken); + if (Entry?.Id != parameters.EntryId) + await GetEntry(parameters.EntryId, cancellationToken); } private async Task GetEntry(long entryId, CancellationToken cancellationToken) @@ -56,10 +57,7 @@ public partial class ProfileDetailsViewModel : RoutableScreen? dependencies = (await _client.GetLatestDependencies.ExecuteAsync(entryId, cancellationToken)).Data?.Entry?.LatestRelease?.Dependencies; - Dependencies = dependencies != null && dependencies.Any() - ? new ReadOnlyObservableCollection(new ObservableCollection(dependencies.Select(_getEntryListViewModel))) - : null; + + await ProfileDescriptionViewModel.SetEntry(Entry, cancellationToken); } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Profile/ProfileListView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Profile/ProfileListView.axaml.cs index 3f038ad94..637a255a6 100644 --- a/src/Artemis.UI/Screens/Workshop/Profile/ProfileListView.axaml.cs +++ b/src/Artemis.UI/Screens/Workshop/Profile/ProfileListView.axaml.cs @@ -1,8 +1,6 @@ using System; using System.Reactive.Disposables; -using Artemis.UI.Shared.Routing; using Avalonia.ReactiveUI; -using Avalonia.Threading; using ReactiveUI; namespace Artemis.UI.Screens.Workshop.Profile; @@ -12,14 +10,8 @@ public partial class ProfileListView : ReactiveUserControl public ProfileListView() { InitializeComponent(); - this.WhenActivated(d => - { - ViewModel.WhenAnyValue(vm => vm.Screen).Subscribe(Navigate).DisposeWith(d); - }); - } - - private void Navigate(RoutableScreen? viewModel) - { - RouterFrame.NavigateFromObject(viewModel ?? ViewModel?.EntryListViewModel); + this.WhenActivated(d => ViewModel.WhenAnyValue(vm => vm.Screen) + .Subscribe(screen => RouterFrame.NavigateFromObject(screen ?? ViewModel?.EntryListViewModel)) + .DisposeWith(d)); } } \ No newline at end of file diff --git a/src/Artemis.WebClient.Workshop/Artemis.WebClient.Workshop.csproj b/src/Artemis.WebClient.Workshop/Artemis.WebClient.Workshop.csproj index 3e348ce4e..a51abe913 100644 --- a/src/Artemis.WebClient.Workshop/Artemis.WebClient.Workshop.csproj +++ b/src/Artemis.WebClient.Workshop/Artemis.WebClient.Workshop.csproj @@ -48,6 +48,9 @@ MSBuild:GenerateGraphQLCode + + MSBuild:GenerateGraphQLCode + diff --git a/src/Artemis.WebClient.Workshop/Queries/GetReleaseById.graphql b/src/Artemis.WebClient.Workshop/Queries/GetReleaseById.graphql new file mode 100644 index 000000000..4b9aaa912 --- /dev/null +++ b/src/Artemis.WebClient.Workshop/Queries/GetReleaseById.graphql @@ -0,0 +1,6 @@ +query GetReleaseById($id: Long!) { + release(id: $id) { + ...release + changelog + } +} \ No newline at end of file diff --git a/src/Artemis.WebClient.Workshop/graphql.config.yml b/src/Artemis.WebClient.Workshop/graphql.config.yml index a8ba99703..9662a514f 100644 --- a/src/Artemis.WebClient.Workshop/graphql.config.yml +++ b/src/Artemis.WebClient.Workshop/graphql.config.yml @@ -2,7 +2,7 @@ schema: schema.graphql extensions: endpoints: Default GraphQL Endpoint: - url: https://localhost:7281/graphql + url: https://workshop.artemis-rgb.com/graphql headers: user-agent: JS GraphQL introspect: true diff --git a/src/Artemis.WebClient.Workshop/schema.graphql b/src/Artemis.WebClient.Workshop/schema.graphql index 442923f3d..57c872355 100644 --- a/src/Artemis.WebClient.Workshop/schema.graphql +++ b/src/Artemis.WebClient.Workshop/schema.graphql @@ -102,6 +102,7 @@ type Mutation { removeLayoutInfo(id: Long!): LayoutInfo! updateEntry(input: UpdateEntryInput!): Entry updateEntryImage(input: UpdateEntryImageInput!): Image + updateRelease(input: UpdateReleaseInput!): Release } "Information about pagination in a connection." @@ -158,6 +159,7 @@ type Query { entry(id: Long!): Entry pluginInfo(pluginGuid: UUID!): PluginInfo pluginInfos(order: [PluginInfoSortInput!], skip: Int, take: Int, where: PluginInfoFilterInput): PluginInfosCollectionSegment + release(id: Long!): Release searchEntries(input: String!, order: [EntrySortInput!], type: EntryType, where: EntryFilterInput): [Entry!]! searchKeyboardLayout(deviceProvider: UUID!, logicalLayout: String, model: String!, physicalLayout: KeyboardLayoutType!, vendor: String!): LayoutInfo searchLayout(deviceProvider: UUID!, deviceType: RGBDeviceType!, model: String!, vendor: String!): LayoutInfo @@ -165,6 +167,7 @@ type Query { } type Release { + changelog: String createdAt: DateTime! dependencies: [Entry!]! downloadSize: Long! @@ -498,6 +501,7 @@ input RGBDeviceTypeOperationFilterInput { input ReleaseFilterInput { and: [ReleaseFilterInput!] + changelog: StringOperationFilterInput createdAt: DateTimeOperationFilterInput dependencies: ListFilterInputTypeOfEntryFilterInput downloadSize: LongOperationFilterInput @@ -511,6 +515,7 @@ input ReleaseFilterInput { } input ReleaseSortInput { + changelog: SortEnumType createdAt: SortEnumType downloadSize: SortEnumType downloads: SortEnumType @@ -558,6 +563,11 @@ input UpdateEntryInput { tags: [String!]! } +input UpdateReleaseInput { + changelog: String + id: Long! +} + input UuidOperationFilterInput { eq: UUID gt: UUID