diff --git a/src/Artemis.UI/Screens/Workshop/Categories/CategoriesView.axaml b/src/Artemis.UI/Screens/Workshop/Categories/CategoriesView.axaml index a9072633d..d629cc7d3 100644 --- a/src/Artemis.UI/Screens/Workshop/Categories/CategoriesView.axaml +++ b/src/Artemis.UI/Screens/Workshop/Categories/CategoriesView.axaml @@ -2,7 +2,20 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:categories="clr-namespace:Artemis.UI.Screens.Workshop.Categories" + xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" - x:Class="Artemis.UI.Screens.Workshop.Categories.CategoriesView"> - Welcome to Avalonia! - + x:Class="Artemis.UI.Screens.Workshop.Categories.CategoriesView" + x:DataType="categories:CategoriesViewModel"> + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Categories/CategoriesView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Categories/CategoriesView.axaml.cs index 0864378dd..15ac17ab5 100644 --- a/src/Artemis.UI/Screens/Workshop/Categories/CategoriesView.axaml.cs +++ b/src/Artemis.UI/Screens/Workshop/Categories/CategoriesView.axaml.cs @@ -1,5 +1,5 @@ using Avalonia; -using Avalonia.Controls; +using Avalonia.Input; using Avalonia.Markup.Xaml; using Avalonia.ReactiveUI; @@ -16,4 +16,10 @@ public partial class CategoriesView : ReactiveUserControl { AvaloniaXamlLoader.Load(this); } + + private void InputElement_OnPointerReleased(object? sender, PointerReleasedEventArgs e) + { + if (e.InitialPressMouseButton == MouseButton.Left && sender is IDataContextProvider p && p.DataContext is CategoryViewModel categoryViewModel) + categoryViewModel.IsSelected = !categoryViewModel.IsSelected; + } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Categories/CategoriesViewModel.cs b/src/Artemis.UI/Screens/Workshop/Categories/CategoriesViewModel.cs index 4306a9fbf..f131958ae 100644 --- a/src/Artemis.UI/Screens/Workshop/Categories/CategoriesViewModel.cs +++ b/src/Artemis.UI/Screens/Workshop/Categories/CategoriesViewModel.cs @@ -1,14 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Disposables; +using System.Threading; +using System.Threading.Tasks; using Artemis.UI.Shared; using Artemis.WebClient.Workshop; +using ReactiveUI; +using Serilog; +using StrawberryShake; namespace Artemis.UI.Screens.Workshop.Categories; public class CategoriesViewModel : ActivatableViewModelBase { private readonly IWorkshopClient _client; + private readonly ILogger _logger; + private IReadOnlyList _categories; - public CategoriesViewModel(IWorkshopClient client) + public CategoriesViewModel(ILogger logger, IWorkshopClient client) { + _logger = logger; _client = client; + + this.WhenActivated(d => ReactiveCommand.CreateFromTask(GetCategories).Execute().Subscribe().DisposeWith(d)); + } + + public IReadOnlyList Categories + { + get => _categories; + set => RaiseAndSetIfChanged(ref _categories, value); + } + + private async Task GetCategories(CancellationToken cancellationToken) + { + try + { + IOperationResult result = await _client.GetCategories.ExecuteAsync(cancellationToken); + if (result.IsErrorResult()) + _logger.Warning("Failed to retrieve categories {Error}", result.Errors); + + Categories = result.Data?.Categories.Select(c => new CategoryViewModel(c)).ToList() ?? new List(); + } + catch (Exception e) + { + _logger.Warning(e, "Failed to retrieve categories"); + } } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Categories/CategoryViewModel.cs b/src/Artemis.UI/Screens/Workshop/Categories/CategoryViewModel.cs new file mode 100644 index 000000000..08d991009 --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Categories/CategoryViewModel.cs @@ -0,0 +1,29 @@ +using System; +using Artemis.UI.Shared; +using Artemis.WebClient.Workshop; +using Material.Icons; + +namespace Artemis.UI.Screens.Workshop.Categories; + +public class CategoryViewModel : ViewModelBase +{ + private bool _isSelected; + + public CategoryViewModel(IGetCategories_Categories category) + { + Id = category.Id; + Name = category.Name; + if (Enum.TryParse(typeof(MaterialIconKind), category.Icon, out object? icon)) + Icon = icon as MaterialIconKind? ?? MaterialIconKind.QuestionMarkCircle; + } + + public int Id { get; } + public string Name { get; } + public MaterialIconKind Icon { get; } + + public bool IsSelected + { + get => _isSelected; + set => RaiseAndSetIfChanged(ref _isSelected, value); + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Layout/LayoutListView.axaml b/src/Artemis.UI/Screens/Workshop/Layout/LayoutListView.axaml index ebbdfd31c..151ec2089 100644 --- a/src/Artemis.UI/Screens/Workshop/Layout/LayoutListView.axaml +++ b/src/Artemis.UI/Screens/Workshop/Layout/LayoutListView.axaml @@ -9,7 +9,10 @@ - Side panel + + Categories + + diff --git a/src/Artemis.UI/Screens/Workshop/Layout/LayoutListViewModel.cs b/src/Artemis.UI/Screens/Workshop/Layout/LayoutListViewModel.cs index b86bf8ecc..17ac0ca9e 100644 --- a/src/Artemis.UI/Screens/Workshop/Layout/LayoutListViewModel.cs +++ b/src/Artemis.UI/Screens/Workshop/Layout/LayoutListViewModel.cs @@ -1,8 +1,8 @@ using System; using System.Threading; using System.Threading.Tasks; +using Artemis.UI.Screens.Workshop.Categories; using Artemis.UI.Screens.Workshop.Parameters; -using Artemis.UI.Screens.Workshop.Search; using Artemis.UI.Shared; using Artemis.UI.Shared.Routing; using Artemis.WebClient.Workshop; @@ -13,11 +13,13 @@ public class LayoutListViewModel : RoutableScreen - public LayoutListViewModel() + public LayoutListViewModel(CategoriesViewModel categoriesViewModel) { + CategoriesViewModel = categoriesViewModel; } + public CategoriesViewModel CategoriesViewModel { get; } + public int Page { get => _page; diff --git a/src/Artemis.UI/Screens/Workshop/Profile/ProfileListEntryView.axaml b/src/Artemis.UI/Screens/Workshop/Profile/ProfileListEntryView.axaml new file mode 100644 index 000000000..13395d7b7 --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Profile/ProfileListEntryView.axaml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Downloads + Last updated + + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Profile/ProfileListEntryView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Profile/ProfileListEntryView.axaml.cs new file mode 100644 index 000000000..288da66ae --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Profile/ProfileListEntryView.axaml.cs @@ -0,0 +1,18 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace Artemis.UI.Screens.Workshop.Profile; + +public partial class ProfileListEntryView : UserControl +{ + public ProfileListEntryView() + { + InitializeComponent(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Profile/ProfileListEntryViewModel.cs b/src/Artemis.UI/Screens/Workshop/Profile/ProfileListEntryViewModel.cs new file mode 100644 index 000000000..67786214d --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Profile/ProfileListEntryViewModel.cs @@ -0,0 +1,14 @@ +using Artemis.UI.Shared; +using Artemis.WebClient.Workshop; + +namespace Artemis.UI.Screens.Workshop.Profile; + +public class ProfileListEntryViewModel : ViewModelBase +{ + public ProfileListEntryViewModel(IGetEntries_Entries_Nodes entry) + { + Entry = entry; + } + + public IGetEntries_Entries_Nodes Entry { get; } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Profile/ProfileListView.axaml b/src/Artemis.UI/Screens/Workshop/Profile/ProfileListView.axaml index d103a3288..38380c8e7 100644 --- a/src/Artemis.UI/Screens/Workshop/Profile/ProfileListView.axaml +++ b/src/Artemis.UI/Screens/Workshop/Profile/ProfileListView.axaml @@ -8,14 +8,22 @@ x:DataType="profile:ProfileListViewModel"> - - Side panel + + + Categories + + + - - - + + + + + + + diff --git a/src/Artemis.UI/Screens/Workshop/Profile/ProfileListViewModel.cs b/src/Artemis.UI/Screens/Workshop/Profile/ProfileListViewModel.cs index d478b5229..76f1fa762 100644 --- a/src/Artemis.UI/Screens/Workshop/Profile/ProfileListViewModel.cs +++ b/src/Artemis.UI/Screens/Workshop/Profile/ProfileListViewModel.cs @@ -1,34 +1,70 @@ using System; +using System.Collections.ObjectModel; +using System.Linq; +using System.Reactive.Disposables; using System.Threading; using System.Threading.Tasks; +using Artemis.UI.Screens.Workshop.Categories; using Artemis.UI.Screens.Workshop.Parameters; -using Artemis.UI.Screens.Workshop.Search; using Artemis.UI.Shared; using Artemis.UI.Shared.Routing; using Artemis.WebClient.Workshop; +using DynamicData; +using ReactiveUI; +using StrawberryShake; namespace Artemis.UI.Screens.Workshop.Profile; public class ProfileListViewModel : RoutableScreen, IWorkshopViewModel { + private readonly SourceList _entries; + private readonly IWorkshopClient _workshopClient; private int _page; - /// - public ProfileListViewModel() + public ProfileListViewModel(IWorkshopClient workshopClient, CategoriesViewModel categoriesViewModel) { - + _workshopClient = workshopClient; + CategoriesViewModel = categoriesViewModel; + + _entries = new SourceList(); + _entries.Connect() + .Transform(e => new ProfileListEntryViewModel(e)) + .Bind(out ReadOnlyObservableCollection observableEntries) + .Subscribe(); + Entries = observableEntries; } + public CategoriesViewModel CategoriesViewModel { get; } + public ReadOnlyObservableCollection Entries { get; set; } + public int Page { get => _page; set => RaiseAndSetIfChanged(ref _page, value); } - public override Task OnNavigating(WorkshopListParameters parameters, NavigationArguments args, CancellationToken cancellationToken) + public override async Task OnNavigating(WorkshopListParameters parameters, NavigationArguments args, CancellationToken cancellationToken) { Page = Math.Max(1, parameters.Page); - return Task.CompletedTask; + await GetEntries(cancellationToken); + } + + private async Task GetEntries(CancellationToken cancellationToken) + { + IOperationResult result = await _workshopClient.GetEntries.ExecuteAsync(CreateFilter(), cancellationToken); + if (result.IsErrorResult() || result.Data?.Entries?.Nodes == null) + return; + + _entries.Edit(e => + { + e.Clear(); + e.AddRange(result.Data.Entries.Nodes); + }); + } + + private EntryFilterInput CreateFilter() + { + return new EntryFilterInput {EntryType = new EntryTypeOperationFilterInput {Eq = WebClient.Workshop.EntryType.Profile}}; } public EntryType? EntryType => null; diff --git a/src/Artemis.WebClient.Workshop/Queries/GetEntries.graphql b/src/Artemis.WebClient.Workshop/Queries/GetEntries.graphql index 9d9457e62..dae8242c6 100644 --- a/src/Artemis.WebClient.Workshop/Queries/GetEntries.graphql +++ b/src/Artemis.WebClient.Workshop/Queries/GetEntries.graphql @@ -1,9 +1,14 @@ -query GetEntries { - entries { +query GetEntries($filter: EntryFilterInput) { + entries(where: $filter) { nodes { author name + summary entryType + categories { + name + icon + } } } } \ No newline at end of file