From 9c04932afa231bbfe5ee012d62c61155cd94bd84 Mon Sep 17 00:00:00 2001 From: Robert Date: Sun, 24 Mar 2024 21:29:48 +0100 Subject: [PATCH] Show multiple releases and improve workshop routing --- src/Artemis.UI/Artemis.UI.csproj | 17 +++++ src/Artemis.UI/Routing/Routes.cs | 2 +- src/Artemis.UI/Screens/Root/RootView.axaml.cs | 2 +- .../Screens/Settings/SettingsView.axaml.cs | 2 +- .../Settings/Tabs/ReleasesTabView.axaml.cs | 18 ++--- .../Entries/Details/EntryInfoViewModel.cs | 5 +- .../Entries/Details/EntryReleasesView.axaml | 72 ++++++++++++++----- .../Entries/Details/EntryReleasesViewModel.cs | 38 +++++++--- .../Workshop/Entries/EntriesView.axaml.cs | 2 +- .../Workshop/Entries/EntriesViewModel.cs | 4 +- .../Workshop/Entries/List/EntryListView.axaml | 57 +++++++++++++++ .../EntryListView.axaml.cs} | 28 +++----- .../Entries/List/EntryListViewModel.cs | 57 +++++++-------- .../Entries/Tabs/LayoutListView.axaml | 67 ----------------- .../Entries/Tabs/LayoutListViewModel.cs | 34 --------- .../Entries/Tabs/PluginListView.axaml | 67 ----------------- .../Entries/Tabs/PluginListView.axaml.cs | 43 ----------- .../Entries/Tabs/PluginListViewModel.cs | 33 --------- .../Entries/Tabs/ProfileListView.axaml | 67 ----------------- .../Entries/Tabs/ProfileListView.axaml.cs | 43 ----------- .../Entries/Tabs/ProfileListViewModel.cs | 33 --------- .../Workshop/Layout/LayoutDetailsView.axaml | 2 +- .../Workshop/Layout/LayoutListView.axaml | 16 +++++ .../Workshop/Layout/LayoutListView.axaml.cs | 25 +++++++ .../Workshop/Layout/LayoutListViewModel.cs | 16 +++++ .../Library/WorkshopLibraryVIew.axaml.cs | 4 +- .../Workshop/Plugins/PluginDetailsView.axaml | 10 ++- .../Plugins/PluginDetailsViewModel.cs | 13 ++-- .../Workshop/Plugins/PluginListView.axaml | 16 +++++ .../Workshop/Plugins/PluginListView.axaml.cs | 24 +++++++ .../Workshop/Plugins/PluginListViewModel.cs | 16 +++++ .../Workshop/Profile/ProfileDetailsView.axaml | 10 ++- .../Workshop/Profile/ProfileListView.axaml | 16 +++++ .../Workshop/Profile/ProfileListView.axaml.cs | 25 +++++++ .../Workshop/Profile/ProfileListViewModel.cs | 16 +++++ .../ReleaseWizardView.axaml.cs | 4 +- .../SubmissionWizardView.axaml.cs | 2 +- src/Artemis.UI/Styles/Markdown.axaml | 5 ++ .../Queries/Fragments.graphql | 6 +- 39 files changed, 419 insertions(+), 498 deletions(-) create mode 100644 src/Artemis.UI/Screens/Workshop/Entries/List/EntryListView.axaml rename src/Artemis.UI/Screens/Workshop/Entries/{Tabs/LayoutListView.axaml.cs => List/EntryListView.axaml.cs} (51%) delete mode 100644 src/Artemis.UI/Screens/Workshop/Entries/Tabs/LayoutListView.axaml delete mode 100644 src/Artemis.UI/Screens/Workshop/Entries/Tabs/LayoutListViewModel.cs delete mode 100644 src/Artemis.UI/Screens/Workshop/Entries/Tabs/PluginListView.axaml delete mode 100644 src/Artemis.UI/Screens/Workshop/Entries/Tabs/PluginListView.axaml.cs delete mode 100644 src/Artemis.UI/Screens/Workshop/Entries/Tabs/PluginListViewModel.cs delete mode 100644 src/Artemis.UI/Screens/Workshop/Entries/Tabs/ProfileListView.axaml delete mode 100644 src/Artemis.UI/Screens/Workshop/Entries/Tabs/ProfileListView.axaml.cs delete mode 100644 src/Artemis.UI/Screens/Workshop/Entries/Tabs/ProfileListViewModel.cs create mode 100644 src/Artemis.UI/Screens/Workshop/Layout/LayoutListView.axaml create mode 100644 src/Artemis.UI/Screens/Workshop/Layout/LayoutListView.axaml.cs create mode 100644 src/Artemis.UI/Screens/Workshop/Layout/LayoutListViewModel.cs create mode 100644 src/Artemis.UI/Screens/Workshop/Plugins/PluginListView.axaml create mode 100644 src/Artemis.UI/Screens/Workshop/Plugins/PluginListView.axaml.cs create mode 100644 src/Artemis.UI/Screens/Workshop/Plugins/PluginListViewModel.cs create mode 100644 src/Artemis.UI/Screens/Workshop/Profile/ProfileListView.axaml create mode 100644 src/Artemis.UI/Screens/Workshop/Profile/ProfileListView.axaml.cs create mode 100644 src/Artemis.UI/Screens/Workshop/Profile/ProfileListViewModel.cs diff --git a/src/Artemis.UI/Artemis.UI.csproj b/src/Artemis.UI/Artemis.UI.csproj index d7bd7aafc..7408b305b 100644 --- a/src/Artemis.UI/Artemis.UI.csproj +++ b/src/Artemis.UI/Artemis.UI.csproj @@ -44,5 +44,22 @@ DeviceSelectionDialogView.axaml Code + + LayoutListView.axaml + Code + + + LayoutListView.axaml + Code + + + LayoutListView.axaml + Code + + + + + + \ No newline at end of file diff --git a/src/Artemis.UI/Routing/Routes.cs b/src/Artemis.UI/Routing/Routes.cs index 8973793b0..09a677350 100644 --- a/src/Artemis.UI/Routing/Routes.cs +++ b/src/Artemis.UI/Routing/Routes.cs @@ -7,11 +7,11 @@ 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.Entries.Tabs; 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.Plugins; using Artemis.UI.Screens.Workshop.Profile; using Artemis.UI.Shared.Routing; using PluginDetailsViewModel = Artemis.UI.Screens.Workshop.Plugins.PluginDetailsViewModel; diff --git a/src/Artemis.UI/Screens/Root/RootView.axaml.cs b/src/Artemis.UI/Screens/Root/RootView.axaml.cs index 04acf1892..90cd6afb8 100644 --- a/src/Artemis.UI/Screens/Root/RootView.axaml.cs +++ b/src/Artemis.UI/Screens/Root/RootView.axaml.cs @@ -19,7 +19,7 @@ public partial class RootView : ReactiveUserControl { try { - Dispatcher.UIThread.Invoke(() => RootFrame.NavigateFromObject(viewModel)); + RootFrame.NavigateFromObject(viewModel); } catch (Exception) { diff --git a/src/Artemis.UI/Screens/Settings/SettingsView.axaml.cs b/src/Artemis.UI/Screens/Settings/SettingsView.axaml.cs index 6e04c8c28..cb03c7a44 100644 --- a/src/Artemis.UI/Screens/Settings/SettingsView.axaml.cs +++ b/src/Artemis.UI/Screens/Settings/SettingsView.axaml.cs @@ -18,7 +18,7 @@ public partial class SettingsView : ReactiveUserControl private void Navigate(ViewModelBase viewModel) { - Dispatcher.UIThread.Invoke(() => TabFrame.NavigateFromObject(viewModel)); + TabFrame.NavigateFromObject(viewModel); } private void NavigationView_OnBackRequested(object? sender, NavigationViewBackRequestedEventArgs e) diff --git a/src/Artemis.UI/Screens/Settings/Tabs/ReleasesTabView.axaml.cs b/src/Artemis.UI/Screens/Settings/Tabs/ReleasesTabView.axaml.cs index cebc6f402..aadd8b4a6 100644 --- a/src/Artemis.UI/Screens/Settings/Tabs/ReleasesTabView.axaml.cs +++ b/src/Artemis.UI/Screens/Settings/Tabs/ReleasesTabView.axaml.cs @@ -17,17 +17,13 @@ public partial class ReleasesTabView : ReactiveUserControl private void Navigate(ViewModelBase viewModel) { - Dispatcher.UIThread.Invoke(() => + try { - try - { - ReleaseFrame.NavigateFromObject(viewModel); - } - catch (Exception) - { - // ignored - } - }); + ReleaseFrame.NavigateFromObject(viewModel); + } + catch (Exception) + { + // ignored + } } - } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryInfoViewModel.cs b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryInfoViewModel.cs index d5c4de433..cd37606ea 100644 --- a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryInfoViewModel.cs +++ b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryInfoViewModel.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Threading.Tasks; using Artemis.Core; using Artemis.UI.Shared; @@ -12,12 +13,12 @@ public class EntryInfoViewModel : ViewModelBase private readonly INotificationService _notificationService; public IEntryDetails Entry { get; } public DateTimeOffset? UpdatedAt { get; } - + public EntryInfoViewModel(IEntryDetails entry, INotificationService notificationService) { _notificationService = notificationService; Entry = entry; - UpdatedAt = Entry.LatestRelease?.CreatedAt ?? Entry.CreatedAt; + UpdatedAt = Entry.Releases.Any() ? Entry.Releases.Max(r => r.CreatedAt) : Entry.CreatedAt; } public async Task CopyShareLink() diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryReleasesView.axaml b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryReleasesView.axaml index 78944e621..5453ac531 100644 --- a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryReleasesView.axaml +++ b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryReleasesView.axaml @@ -6,6 +6,7 @@ xmlns:converters="clr-namespace:Artemis.UI.Converters" xmlns:sharedConverters="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared" xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" + xmlns:workshop="clr-namespace:Artemis.WebClient.Workshop;assembly=Artemis.WebClient.Workshop" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="Artemis.UI.Screens.Workshop.Entries.Details.EntryReleasesView" x:DataType="details:EntryReleasesViewModel"> @@ -14,39 +15,74 @@ - Latest release + Releases - + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryReleasesViewModel.cs b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryReleasesViewModel.cs index 12bd22b3a..8d35a9f38 100644 --- a/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryReleasesViewModel.cs +++ b/src/Artemis.UI/Screens/Workshop/Entries/Details/EntryReleasesViewModel.cs @@ -1,8 +1,11 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Reactive; using System.Threading; using System.Threading.Tasks; using Artemis.UI.Shared; +using Artemis.UI.Shared.Routing; using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services.Builders; using Artemis.UI.Shared.Utilities; @@ -19,34 +22,49 @@ public class EntryReleasesViewModel : ViewModelBase private readonly EntryInstallationHandlerFactory _factory; private readonly IWindowService _windowService; private readonly INotificationService _notificationService; + private readonly IRouter _router; - public EntryReleasesViewModel(IEntryDetails entry, EntryInstallationHandlerFactory factory, IWindowService windowService, INotificationService notificationService) + public EntryReleasesViewModel(IEntryDetails entry, EntryInstallationHandlerFactory factory, IWindowService windowService, INotificationService notificationService, IRouter router) { _factory = factory; _windowService = windowService; _notificationService = notificationService; + _router = router; Entry = entry; + LatestRelease = Entry.Releases.MaxBy(r => r.CreatedAt); + OtherReleases = Entry.Releases.OrderByDescending(r => r.CreatedAt).Skip(1).Take(4).Cast().ToList(); + DownloadLatestRelease = ReactiveCommand.CreateFromTask(ExecuteDownloadLatestRelease); OnInstallationStarted = Confirm; + NavigateToRelease = ReactiveCommand.CreateFromTask(ExecuteNavigateToRelease); } public IEntryDetails Entry { get; } - public ReactiveCommand DownloadLatestRelease { get; } + public IRelease? LatestRelease { get; } + public List OtherReleases { get; } - public Func> OnInstallationStarted { get; set; } + public ReactiveCommand DownloadLatestRelease { get; } + public ReactiveCommand NavigateToRelease { get; } + + public Func> OnInstallationStarted { get; set; } public Func? OnInstallationFinished { get; set; } + private async Task ExecuteNavigateToRelease(IRelease release) + { + await _router.Navigate($"workshop/entries/{Entry.Id}/releases/{release.Id}"); + } + private async Task ExecuteDownloadLatestRelease(CancellationToken cancellationToken) { - if (Entry.LatestRelease == null) + if (LatestRelease == null) return; - - if (await OnInstallationStarted(Entry)) + + if (await OnInstallationStarted(Entry, LatestRelease)) return; IEntryInstallationHandler installationHandler = _factory.CreateHandler(Entry.EntryType); - EntryInstallResult result = await installationHandler.InstallAsync(Entry, Entry.LatestRelease, new Progress(), cancellationToken); + EntryInstallResult result = await installationHandler.InstallAsync(Entry, LatestRelease, new Progress(), cancellationToken); if (result.IsSuccess && result.Entry != null) { if (OnInstallationFinished != null) @@ -62,13 +80,13 @@ public class EntryReleasesViewModel : ViewModelBase } } - private async Task Confirm(IEntryDetails entryDetails) + private async Task Confirm(IEntryDetails entryDetails, IRelease release) { bool confirm = await _windowService.ShowConfirmContentDialog( "Install latest release", - $"Are you sure you want to download and install version {entryDetails.LatestRelease?.Version} of {entryDetails.Name}?" + $"Are you sure you want to download and install version {release.Version} of {entryDetails.Name}?" ); - + return !confirm; } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Entries/EntriesView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Entries/EntriesView.axaml.cs index 3f2106645..1299973d2 100644 --- a/src/Artemis.UI/Screens/Workshop/Entries/EntriesView.axaml.cs +++ b/src/Artemis.UI/Screens/Workshop/Entries/EntriesView.axaml.cs @@ -18,7 +18,7 @@ public partial class EntriesView : ReactiveUserControl private void Navigate(ViewModelBase viewModel) { - Dispatcher.UIThread.Invoke(() => TabFrame.NavigateFromObject(viewModel)); + TabFrame.NavigateFromObject(viewModel); } private void NavigationView_OnBackRequested(object? sender, NavigationViewBackRequestedEventArgs e) diff --git a/src/Artemis.UI/Screens/Workshop/Entries/EntriesViewModel.cs b/src/Artemis.UI/Screens/Workshop/Entries/EntriesViewModel.cs index e76014c34..760193832 100644 --- a/src/Artemis.UI/Screens/Workshop/Entries/EntriesViewModel.cs +++ b/src/Artemis.UI/Screens/Workshop/Entries/EntriesViewModel.cs @@ -53,8 +53,8 @@ public partial class EntriesViewModel : RoutableHostScreen public void GoBack() { - if (ViewingDetails) - _router.GoBack(); + if (ViewingDetails && SelectedTab != null) + _router.Navigate(SelectedTab.Path); else _router.Navigate("workshop"); } diff --git a/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListView.axaml b/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListView.axaml new file mode 100644 index 000000000..597282d98 --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListView.axaml @@ -0,0 +1,57 @@ + + + + + + + + + + + + Categories + + + + + + + + + + + + + + + + + + + + + + + + + + + Looks like your current filters gave no results + + Modify or clear your filters to view other entries + + + + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Tabs/LayoutListView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListView.axaml.cs similarity index 51% rename from src/Artemis.UI/Screens/Workshop/Entries/Tabs/LayoutListView.axaml.cs rename to src/Artemis.UI/Screens/Workshop/Entries/List/EntryListView.axaml.cs index a003f47bb..e4a15ed77 100644 --- a/src/Artemis.UI/Screens/Workshop/Entries/Tabs/LayoutListView.axaml.cs +++ b/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListView.axaml.cs @@ -1,38 +1,30 @@ -using System; -using System.Reactive.Disposables; using System.Threading; -using Artemis.UI.Shared.Routing; using Avalonia.Controls; using Avalonia.ReactiveUI; -using Avalonia.Threading; using ReactiveUI; -namespace Artemis.UI.Screens.Workshop.Entries.Tabs; +namespace Artemis.UI.Screens.Workshop.Entries.List; -public partial class LayoutListView : ReactiveUserControl +public partial class EntryListView : ReactiveUserControl { - public LayoutListView() + public EntryListView() { InitializeComponent(); EntriesScrollViewer.SizeChanged += (_, _) => UpdateEntriesPerFetch(); - - this.WhenActivated(d => - { - UpdateEntriesPerFetch(); - ViewModel.WhenAnyValue(vm => vm.Screen).WhereNotNull().Subscribe(Navigate).DisposeWith(d); - }); + + this.WhenActivated(_ => UpdateEntriesPerFetch()); } private void ScrollViewer_OnScrollChanged(object? sender, ScrollChangedEventArgs e) { + if (ViewModel == null) + return; + // When near the bottom of EntriesScrollViewer, call FetchMore on the view model if (EntriesScrollViewer.Offset.Y != 0 && EntriesScrollViewer.Extent.Height - (EntriesScrollViewer.Viewport.Height + EntriesScrollViewer.Offset.Y) < 100) - ViewModel?.FetchMore(CancellationToken.None); - } + ViewModel.FetchMore(CancellationToken.None); - private void Navigate(RoutableScreen viewModel) - { - Dispatcher.UIThread.Invoke(() => RouterFrame.NavigateFromObject(viewModel), DispatcherPriority.ApplicationIdle); + ViewModel.ScrollOffset = EntriesScrollViewer.Offset; } private void UpdateEntriesPerFetch() diff --git a/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListViewModel.cs b/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListViewModel.cs index 92322920d..f335c9128 100644 --- a/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListViewModel.cs +++ b/src/Artemis.UI/Screens/Workshop/Entries/List/EntryListViewModel.cs @@ -6,6 +6,7 @@ using System.Reactive.Disposables; using System.Reactive.Linq; using System.Threading; using System.Threading.Tasks; +using Artemis.UI.Extensions; using Artemis.UI.Screens.Workshop.Categories; using Artemis.UI.Shared.Routing; using Artemis.UI.Shared.Services; @@ -15,29 +16,28 @@ using DynamicData; using PropertyChanged.SourceGenerator; using ReactiveUI; using StrawberryShake; +using Vector = Avalonia.Vector; namespace Artemis.UI.Screens.Workshop.Entries.List; -public abstract partial class EntryListViewModel : RoutableHostScreen +public partial class EntryListViewModel : RoutableScreen { private readonly SourceList _entries = new(); private readonly INotificationService _notificationService; private readonly IWorkshopClient _workshopClient; - private readonly string _route; private IGetEntriesv2_EntriesV2_PageInfo? _currentPageInfo; [Notify] private bool _initializing = true; [Notify] private bool _fetchingMore; [Notify] private int _entriesPerFetch; + [Notify] private Vector _scrollOffset; - protected EntryListViewModel(string route, - IWorkshopClient workshopClient, + protected EntryListViewModel(IWorkshopClient workshopClient, CategoriesViewModel categoriesViewModel, EntryListInputViewModel entryListInputViewModel, INotificationService notificationService, Func getEntryListViewModel) { - _route = route; _workshopClient = workshopClient; _notificationService = notificationService; @@ -50,37 +50,31 @@ public abstract partial class EntryListViewModel : RoutableHostScreen { - // Respond to filter query input changes InputViewModel.WhenAnyValue(vm => vm.Search).Skip(1).Throttle(TimeSpan.FromMilliseconds(200)).Subscribe(_ => Reset()).DisposeWith(d); CategoriesViewModel.WhenAnyValue(vm => vm.CategoryFilters).Skip(1).Subscribe(_ => Reset()).DisposeWith(d); }); + + // Load entries when the view model is first activated + this.WhenActivatedAsync(async _ => + { + if (_entries.Count == 0) + { + await Task.Delay(250); + await FetchMore(CancellationToken.None); + Initializing = false; + } + }); } public CategoriesViewModel CategoriesViewModel { get; } public EntryListInputViewModel InputViewModel { get; } + public EntryType? EntryType { get; set; } public ReadOnlyObservableCollection Entries { get; } - - public override async Task OnNavigating(NavigationArguments args, CancellationToken cancellationToken) - { - if (_entries.Count == 0) - { - await Task.Delay(250, cancellationToken); - await FetchMore(cancellationToken); - Initializing = false; - } - } - - public override Task OnClosing(NavigationArguments args) - { - // Clear search if not navigating to a child - if (!args.Path.StartsWith(_route)) - InputViewModel.ClearLastSearch(); - return base.OnClosing(args); - } - + public async Task FetchMore(CancellationToken cancellationToken) { if (FetchingMore || _currentPageInfo != null && !_currentPageInfo.HasNextPage) @@ -119,12 +113,19 @@ public abstract partial class EntryListViewModel : RoutableHostScreen GetSort() + private IReadOnlyList GetSort() { // Sort by created at if (InputViewModel.SortBy == 1) diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Tabs/LayoutListView.axaml b/src/Artemis.UI/Screens/Workshop/Entries/Tabs/LayoutListView.axaml deleted file mode 100644 index 1c0c4182c..000000000 --- a/src/Artemis.UI/Screens/Workshop/Entries/Tabs/LayoutListView.axaml +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - Categories - - - - - - - - - - - - - - - - - - - - - - - - - - - Looks like your current filters gave no results - - Modify or clear your filters to view other device layouts - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Tabs/LayoutListViewModel.cs b/src/Artemis.UI/Screens/Workshop/Entries/Tabs/LayoutListViewModel.cs deleted file mode 100644 index 11c97846c..000000000 --- a/src/Artemis.UI/Screens/Workshop/Entries/Tabs/LayoutListViewModel.cs +++ /dev/null @@ -1,34 +0,0 @@ -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 LayoutListViewModel : List.EntryListViewModel -{ - public LayoutListViewModel(IWorkshopClient workshopClient, - CategoriesViewModel categoriesViewModel, - EntryListInputViewModel entryListInputViewModel, - INotificationService notificationService, - Func getEntryListViewModel) - : base("workshop/entries/layouts", workshopClient, categoriesViewModel, entryListInputViewModel, notificationService, getEntryListViewModel) - { - entryListInputViewModel.SearchWatermark = "Search layouts"; - } - - protected override EntryFilterInput GetFilter() - { - return new EntryFilterInput - { - And = new[] - { - new EntryFilterInput {EntryType = new EntryTypeOperationFilterInput {Eq = EntryType.Layout}}, - new EntryFilterInput(){LatestReleaseId = new LongOperationFilterInput {Gt = 0}}, - base.GetFilter() - } - }; - } -} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Tabs/PluginListView.axaml b/src/Artemis.UI/Screens/Workshop/Entries/Tabs/PluginListView.axaml deleted file mode 100644 index 7b6cb1f0c..000000000 --- a/src/Artemis.UI/Screens/Workshop/Entries/Tabs/PluginListView.axaml +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - Categories - - - - - - - - - - - - - - - - - - - - - - - - - - - Looks like your current filters gave no results - - Modify or clear your filters to view other plugins - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Tabs/PluginListView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Entries/Tabs/PluginListView.axaml.cs deleted file mode 100644 index 8cfaa1696..000000000 --- a/src/Artemis.UI/Screens/Workshop/Entries/Tabs/PluginListView.axaml.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Reactive.Disposables; -using System.Threading; -using Artemis.UI.Shared.Routing; -using Avalonia.Controls; -using Avalonia.ReactiveUI; -using Avalonia.Threading; -using ReactiveUI; - -namespace Artemis.UI.Screens.Workshop.Entries.Tabs; - -public partial class PluginListView : ReactiveUserControl -{ - public PluginListView() - { - InitializeComponent(); - EntriesScrollViewer.SizeChanged += (_, _) => UpdateEntriesPerFetch(); - - this.WhenActivated(d => - { - UpdateEntriesPerFetch(); - ViewModel.WhenAnyValue(vm => vm.Screen).WhereNotNull().Subscribe(Navigate).DisposeWith(d); - }); - } - - private void ScrollViewer_OnScrollChanged(object? sender, ScrollChangedEventArgs e) - { - // When near the bottom of EntriesScrollViewer, call FetchMore on the view model - if (EntriesScrollViewer.Offset.Y != 0 && EntriesScrollViewer.Extent.Height - (EntriesScrollViewer.Viewport.Height + EntriesScrollViewer.Offset.Y) < 100) - ViewModel?.FetchMore(CancellationToken.None); - } - - private void Navigate(RoutableScreen viewModel) - { - Dispatcher.UIThread.Invoke(() => RouterFrame.NavigateFromObject(viewModel), DispatcherPriority.ApplicationIdle); - } - - private void UpdateEntriesPerFetch() - { - if (ViewModel != null) - ViewModel.EntriesPerFetch = (int) (EntriesScrollViewer.Viewport.Height / 120); - } -} \ 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 deleted file mode 100644 index c7ea484a6..000000000 --- a/src/Artemis.UI/Screens/Workshop/Entries/Tabs/PluginListViewModel.cs +++ /dev/null @@ -1,33 +0,0 @@ -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, - CategoriesViewModel categoriesViewModel, - EntryListInputViewModel entryListInputViewModel, - INotificationService notificationService, - Func getEntryListViewModel) - : base("workshop/entries/plugins", workshopClient, 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/ProfileListView.axaml b/src/Artemis.UI/Screens/Workshop/Entries/Tabs/ProfileListView.axaml deleted file mode 100644 index 03028d4b8..000000000 --- a/src/Artemis.UI/Screens/Workshop/Entries/Tabs/ProfileListView.axaml +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - Categories - - - - - - - - - - - - - - - - - - - - - - - - - - - Looks like your current filters gave no results - - Modify or clear your filters to view some awesome profiles - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Entries/Tabs/ProfileListView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Entries/Tabs/ProfileListView.axaml.cs deleted file mode 100644 index b25ba45f3..000000000 --- a/src/Artemis.UI/Screens/Workshop/Entries/Tabs/ProfileListView.axaml.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Reactive.Disposables; -using System.Threading; -using Artemis.UI.Shared.Routing; -using Avalonia.Controls; -using Avalonia.ReactiveUI; -using Avalonia.Threading; -using ReactiveUI; - -namespace Artemis.UI.Screens.Workshop.Entries.Tabs; - -public partial class ProfileListView : ReactiveUserControl -{ - public ProfileListView() - { - InitializeComponent(); - EntriesScrollViewer.SizeChanged += (_, _) => UpdateEntriesPerFetch(); - - this.WhenActivated(d => - { - UpdateEntriesPerFetch(); - ViewModel.WhenAnyValue(vm => vm.Screen).WhereNotNull().Subscribe(Navigate).DisposeWith(d); - }); - } - - private void ScrollViewer_OnScrollChanged(object? sender, ScrollChangedEventArgs e) - { - // When near the bottom of EntriesScrollViewer, call FetchMore on the view model - if (EntriesScrollViewer.Offset.Y != 0 && EntriesScrollViewer.Extent.Height - (EntriesScrollViewer.Viewport.Height + EntriesScrollViewer.Offset.Y) < 100) - ViewModel?.FetchMore(CancellationToken.None); - } - - private void Navigate(RoutableScreen viewModel) - { - Dispatcher.UIThread.Invoke(() => RouterFrame.NavigateFromObject(viewModel), DispatcherPriority.ApplicationIdle); - } - - private void UpdateEntriesPerFetch() - { - if (ViewModel != null) - ViewModel.EntriesPerFetch = (int) (EntriesScrollViewer.Viewport.Height / 120); - } -} \ 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 deleted file mode 100644 index 09ed5410b..000000000 --- a/src/Artemis.UI/Screens/Workshop/Entries/Tabs/ProfileListViewModel.cs +++ /dev/null @@ -1,33 +0,0 @@ -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 ProfileListViewModel : List.EntryListViewModel -{ - public ProfileListViewModel(IWorkshopClient workshopClient, - CategoriesViewModel categoriesViewModel, - EntryListInputViewModel entryListInputViewModel, - INotificationService notificationService, - Func getEntryListViewModel) - : base("workshop/entries/profiles", workshopClient, categoriesViewModel, entryListInputViewModel, notificationService, getEntryListViewModel) - { - entryListInputViewModel.SearchWatermark = "Search profiles"; - } - - protected override EntryFilterInput GetFilter() - { - return new EntryFilterInput - { - And = new[] - { - new EntryFilterInput {EntryType = new EntryTypeOperationFilterInput {Eq = EntryType.Profile}}, - base.GetFilter() - } - }; - } -} \ 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 3dd205871..c8ed98401 100644 --- a/src/Artemis.UI/Screens/Workshop/Layout/LayoutDetailsView.axaml +++ b/src/Artemis.UI/Screens/Workshop/Layout/LayoutDetailsView.axaml @@ -12,7 +12,7 @@ - + diff --git a/src/Artemis.UI/Screens/Workshop/Layout/LayoutListView.axaml b/src/Artemis.UI/Screens/Workshop/Layout/LayoutListView.axaml new file mode 100644 index 000000000..6c1b6e8ae --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Layout/LayoutListView.axaml @@ -0,0 +1,16 @@ + + + + + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Layout/LayoutListView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Layout/LayoutListView.axaml.cs new file mode 100644 index 000000000..4adafd4f7 --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Layout/LayoutListView.axaml.cs @@ -0,0 +1,25 @@ +using System; +using System.Reactive.Disposables; +using Artemis.UI.Shared.Routing; +using Avalonia.ReactiveUI; +using Avalonia.Threading; +using ReactiveUI; + +namespace Artemis.UI.Screens.Workshop.Layout; + +public partial class LayoutListView : ReactiveUserControl +{ + 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); + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Layout/LayoutListViewModel.cs b/src/Artemis.UI/Screens/Workshop/Layout/LayoutListViewModel.cs new file mode 100644 index 000000000..52806ca6a --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Layout/LayoutListViewModel.cs @@ -0,0 +1,16 @@ +using Artemis.UI.Screens.Workshop.Entries.List; +using Artemis.UI.Shared.Routing; +using Artemis.WebClient.Workshop; + +namespace Artemis.UI.Screens.Workshop.Layout; + +public class LayoutListViewModel : RoutableHostScreen +{ + public EntryListViewModel EntryListViewModel { get; } + + public LayoutListViewModel(EntryListViewModel entryListViewModel) + { + EntryListViewModel = entryListViewModel; + EntryListViewModel.EntryType = EntryType.Layout; + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Library/WorkshopLibraryVIew.axaml.cs b/src/Artemis.UI/Screens/Workshop/Library/WorkshopLibraryVIew.axaml.cs index 7758b5729..5fa303000 100644 --- a/src/Artemis.UI/Screens/Workshop/Library/WorkshopLibraryVIew.axaml.cs +++ b/src/Artemis.UI/Screens/Workshop/Library/WorkshopLibraryVIew.axaml.cs @@ -18,9 +18,9 @@ public partial class WorkshopLibraryView : ReactiveUserControl TabFrame.NavigateFromObject(viewModel)); + TabFrame.NavigateFromObject(viewModel); } - + private void NavigationView_OnBackRequested(object? sender, NavigationViewBackRequestedEventArgs e) { ViewModel?.GoBack(); diff --git a/src/Artemis.UI/Screens/Workshop/Plugins/PluginDetailsView.axaml b/src/Artemis.UI/Screens/Workshop/Plugins/PluginDetailsView.axaml index dcbb4f5d1..1aa45690d 100644 --- a/src/Artemis.UI/Screens/Workshop/Plugins/PluginDetailsView.axaml +++ b/src/Artemis.UI/Screens/Workshop/Plugins/PluginDetailsView.axaml @@ -29,7 +29,7 @@ - + @@ -49,7 +49,13 @@ Used by these profiles - + + + + + + + diff --git a/src/Artemis.UI/Screens/Workshop/Plugins/PluginDetailsViewModel.cs b/src/Artemis.UI/Screens/Workshop/Plugins/PluginDetailsViewModel.cs index 64b556639..9a2d94314 100644 --- a/src/Artemis.UI/Screens/Workshop/Plugins/PluginDetailsViewModel.cs +++ b/src/Artemis.UI/Screens/Workshop/Plugins/PluginDetailsViewModel.cs @@ -33,7 +33,7 @@ public partial class PluginDetailsViewModel : RoutableScreen? _dependants; - + public PluginDetailsViewModel(IWorkshopClient client, IWindowService windowService, IPluginManagementService pluginManagementService, @@ -72,18 +72,21 @@ 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))) + ? new ReadOnlyObservableCollection(new ObservableCollection(dependants + .Select(_getEntryListViewModel) + .OrderByDescending(d => d.Entry.Downloads) + .Take(10))) : null; } - private async Task OnInstallationStarted(IEntryDetails entryDetails) + private async Task OnInstallationStarted(IEntryDetails entryDetails, IRelease release) { bool confirm = await _windowService.ShowConfirmContentDialog( "Installing plugin", - $"You are about to install version {entryDetails.LatestRelease?.Version} of {entryDetails.Name}. \r\n\r\n" + + $"You are about to install version {release.Version} of {entryDetails.Name}. \r\n\r\n" + "Plugins are NOT verified by Artemis and could harm your PC, if you have doubts about a plugin please ask on Discord!", "I trust this plugin, install it" ); diff --git a/src/Artemis.UI/Screens/Workshop/Plugins/PluginListView.axaml b/src/Artemis.UI/Screens/Workshop/Plugins/PluginListView.axaml new file mode 100644 index 000000000..da72fd3fe --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Plugins/PluginListView.axaml @@ -0,0 +1,16 @@ + + + + + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Plugins/PluginListView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Plugins/PluginListView.axaml.cs new file mode 100644 index 000000000..27b52c560 --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Plugins/PluginListView.axaml.cs @@ -0,0 +1,24 @@ +using System; +using System.Reactive.Disposables; +using Artemis.UI.Shared.Routing; +using Avalonia.ReactiveUI; +using ReactiveUI; + +namespace Artemis.UI.Screens.Workshop.Plugins; + +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); + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Plugins/PluginListViewModel.cs b/src/Artemis.UI/Screens/Workshop/Plugins/PluginListViewModel.cs new file mode 100644 index 000000000..a5e1216ec --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Plugins/PluginListViewModel.cs @@ -0,0 +1,16 @@ +using Artemis.UI.Screens.Workshop.Entries.List; +using Artemis.UI.Shared.Routing; +using Artemis.WebClient.Workshop; + +namespace Artemis.UI.Screens.Workshop.Plugins; + +public class PluginListViewModel : RoutableHostScreen +{ + public EntryListViewModel EntryListViewModel { get; } + + public PluginListViewModel(EntryListViewModel entryListViewModel) + { + EntryListViewModel = entryListViewModel; + EntryListViewModel.EntryType = EntryType.Plugin; + } +} \ 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 499a5b978..9220a2b49 100644 --- a/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsView.axaml +++ b/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsView.axaml @@ -12,7 +12,7 @@ - + @@ -32,7 +32,13 @@ Required plugins - + + + + + + + diff --git a/src/Artemis.UI/Screens/Workshop/Profile/ProfileListView.axaml b/src/Artemis.UI/Screens/Workshop/Profile/ProfileListView.axaml new file mode 100644 index 000000000..70ca1997a --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Profile/ProfileListView.axaml @@ -0,0 +1,16 @@ + + + + + + + \ 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 new file mode 100644 index 000000000..3f038ad94 --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Profile/ProfileListView.axaml.cs @@ -0,0 +1,25 @@ +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; + +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); + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Profile/ProfileListViewModel.cs b/src/Artemis.UI/Screens/Workshop/Profile/ProfileListViewModel.cs new file mode 100644 index 000000000..f012bf65c --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Profile/ProfileListViewModel.cs @@ -0,0 +1,16 @@ +using Artemis.UI.Screens.Workshop.Entries.List; +using Artemis.UI.Shared.Routing; +using Artemis.WebClient.Workshop; + +namespace Artemis.UI.Screens.Workshop.Profile; + +public class ProfileListViewModel : RoutableHostScreen +{ + public EntryListViewModel EntryListViewModel { get; } + + public ProfileListViewModel(EntryListViewModel entryListViewModel) + { + EntryListViewModel = entryListViewModel; + EntryListViewModel.EntryType = EntryType.Profile; + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/ReleaseWizardView.axaml.cs b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/ReleaseWizardView.axaml.cs index 50bbc4ace..ca6667b66 100644 --- a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/ReleaseWizardView.axaml.cs +++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/ReleaseWizardView.axaml.cs @@ -8,7 +8,7 @@ using ReactiveUI; namespace Artemis.UI.Screens.Workshop.SubmissionWizard; -public partial class ReleaseWizardView: ReactiveAppWindow +public partial class ReleaseWizardView : ReactiveAppWindow { public ReleaseWizardView() { @@ -25,7 +25,7 @@ public partial class ReleaseWizardView: ReactiveAppWindow Frame.NavigateFromObject(viewModel)); + Frame.NavigateFromObject(viewModel); } catch (Exception e) { diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/SubmissionWizardView.axaml.cs b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/SubmissionWizardView.axaml.cs index 6c7ece92f..377bc17cb 100644 --- a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/SubmissionWizardView.axaml.cs +++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/SubmissionWizardView.axaml.cs @@ -25,7 +25,7 @@ public partial class SubmissionWizardView : ReactiveAppWindow Frame.NavigateFromObject(viewModel)); + Frame.NavigateFromObject(viewModel); } catch (Exception e) { diff --git a/src/Artemis.UI/Styles/Markdown.axaml b/src/Artemis.UI/Styles/Markdown.axaml index 6f03f8291..9f1bd2470 100644 --- a/src/Artemis.UI/Styles/Markdown.axaml +++ b/src/Artemis.UI/Styles/Markdown.axaml @@ -2,6 +2,11 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:avalonia="clr-namespace:Markdown.Avalonia;assembly=Markdown.Avalonia" xmlns:ctxt="clr-namespace:ColorTextBlock.Avalonia;assembly=ColorTextBlock.Avalonia"> + + + Test + + diff --git a/src/Artemis.WebClient.Workshop/Queries/Fragments.graphql b/src/Artemis.WebClient.Workshop/Queries/Fragments.graphql index 0325293c5..ea41aa36a 100644 --- a/src/Artemis.WebClient.Workshop/Queries/Fragments.graphql +++ b/src/Artemis.WebClient.Workshop/Queries/Fragments.graphql @@ -54,12 +54,12 @@ fragment entryDetails on Entry { categories { ...category } - latestRelease { - ...release - } images { ...image } + releases { + ...release + } } fragment release on Release {