mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-12 21:38:38 +00:00
Workshop - Added filtering, sorting and changable entries per page
This commit is contained in:
parent
aa8519b33c
commit
4994b3fb44
@ -12,18 +12,24 @@ using Avalonia;
|
|||||||
using Avalonia.Controls.ApplicationLifetimes;
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using DryIoc;
|
using DryIoc;
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
namespace Artemis.UI.Linux;
|
namespace Artemis.UI.Linux;
|
||||||
|
|
||||||
public class ApplicationStateManager
|
public class ApplicationStateManager
|
||||||
{
|
{
|
||||||
|
private readonly IContainer _container;
|
||||||
|
private readonly ILogger _logger;
|
||||||
private readonly IWindowService _windowService;
|
private readonly IWindowService _windowService;
|
||||||
|
|
||||||
|
|
||||||
// ReSharper disable once NotAccessedField.Local - Kept in scope to ensure it does not get released
|
// ReSharper disable once NotAccessedField.Local - Kept in scope to ensure it does not get released
|
||||||
private Mutex? _artemisMutex;
|
private Mutex? _artemisMutex;
|
||||||
|
|
||||||
public ApplicationStateManager(IContainer container, string[] startupArguments)
|
public ApplicationStateManager(IContainer container, string[] startupArguments)
|
||||||
{
|
{
|
||||||
|
_container = container;
|
||||||
|
_logger = container.Resolve<ILogger>();
|
||||||
_windowService = container.Resolve<IWindowService>();
|
_windowService = container.Resolve<IWindowService>();
|
||||||
StartupArguments = startupArguments;
|
StartupArguments = startupArguments;
|
||||||
|
|
||||||
@ -33,14 +39,7 @@ public class ApplicationStateManager
|
|||||||
|
|
||||||
// On OS shutdown dispose the IOC container just so device providers get a chance to clean up
|
// On OS shutdown dispose the IOC container just so device providers get a chance to clean up
|
||||||
if (Application.Current?.ApplicationLifetime is IControlledApplicationLifetime controlledApplicationLifetime)
|
if (Application.Current?.ApplicationLifetime is IControlledApplicationLifetime controlledApplicationLifetime)
|
||||||
controlledApplicationLifetime.Exit += (_, _) =>
|
controlledApplicationLifetime.Exit += ControlledApplicationLifetimeOnExit;
|
||||||
{
|
|
||||||
RunForcedShutdownIfEnabled();
|
|
||||||
|
|
||||||
// Dispose plugins before disposing the IOC container because plugins might access services during dispose
|
|
||||||
container.Resolve<IPluginManagementService>().Dispose();
|
|
||||||
container.Dispose();
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string[] StartupArguments { get; }
|
public string[] StartupArguments { get; }
|
||||||
@ -124,6 +123,17 @@ public class ApplicationStateManager
|
|||||||
Dispatcher.UIThread.Post(() => controlledApplicationLifetime.Shutdown());
|
Dispatcher.UIThread.Post(() => controlledApplicationLifetime.Shutdown());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ControlledApplicationLifetimeOnExit(object? sender, ControlledApplicationLifetimeExitEventArgs e)
|
||||||
|
{
|
||||||
|
_logger.Information("Application lifetime exiting, disposing container and friends");
|
||||||
|
|
||||||
|
RunForcedShutdownIfEnabled();
|
||||||
|
|
||||||
|
// Dispose plugins before disposing the IOC container because plugins might access services during dispose
|
||||||
|
_container.Resolve<IPluginManagementService>().Dispose();
|
||||||
|
_container.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
private void UtilitiesOnShutdownRequested(object? sender, EventArgs e)
|
private void UtilitiesOnShutdownRequested(object? sender, EventArgs e)
|
||||||
{
|
{
|
||||||
RunForcedShutdownIfEnabled();
|
RunForcedShutdownIfEnabled();
|
||||||
|
|||||||
@ -177,6 +177,8 @@ internal class Router : CorePropertyChanged, IRouter, IDisposable
|
|||||||
_currentRouteSubject.Dispose();
|
_currentRouteSubject.Dispose();
|
||||||
_mainWindowService.MainWindowOpened -= MainWindowServiceOnMainWindowOpened;
|
_mainWindowService.MainWindowOpened -= MainWindowServiceOnMainWindowOpened;
|
||||||
_mainWindowService.MainWindowClosed -= MainWindowServiceOnMainWindowClosed;
|
_mainWindowService.MainWindowClosed -= MainWindowServiceOnMainWindowClosed;
|
||||||
|
|
||||||
|
_logger.Debug("Router disposed, should that be? Stacktrace: \r\n{StackTrace}", Environment.StackTrace);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MainWindowServiceOnMainWindowOpened(object? sender, EventArgs e)
|
private void MainWindowServiceOnMainWindowOpened(object? sender, EventArgs e)
|
||||||
|
|||||||
@ -137,4 +137,23 @@
|
|||||||
<Setter Property="Margin" Value="4 0 0 0"></Setter>
|
<Setter Property="Margin" Value="4 0 0 0"></Setter>
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="TextBox.search-box">
|
||||||
|
<Setter Property="VerticalAlignment" Value="Top"></Setter>
|
||||||
|
<Setter Property="InnerRightContent">
|
||||||
|
<Template>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Button Content=""
|
||||||
|
FontFamily="{StaticResource SymbolThemeFontFamily}"
|
||||||
|
Theme="{StaticResource TransparentButton}"
|
||||||
|
Command="{CompiledBinding $parent[TextBox].Clear}"
|
||||||
|
IsVisible="{CompiledBinding Text, RelativeSource={RelativeSource FindAncestor, AncestorType=TextBox}, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
|
||||||
|
<Button Content=""
|
||||||
|
FontFamily="{StaticResource SymbolThemeFontFamily}"
|
||||||
|
Theme="{StaticResource TransparentButton}"
|
||||||
|
Command="{CompiledBinding $parent[TextBox].Clear}"
|
||||||
|
IsHitTestVisible="False" />
|
||||||
|
</StackPanel>
|
||||||
|
</Template>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
</Styles>
|
</Styles>
|
||||||
@ -11,15 +11,21 @@ using Avalonia;
|
|||||||
using Avalonia.Controls.ApplicationLifetimes;
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using DryIoc;
|
using DryIoc;
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
namespace Artemis.UI.Windows;
|
namespace Artemis.UI.Windows;
|
||||||
|
|
||||||
public class ApplicationStateManager
|
public class ApplicationStateManager
|
||||||
{
|
{
|
||||||
|
private readonly IContainer _container;
|
||||||
|
private readonly ILogger _logger;
|
||||||
private const int SM_SHUTTINGDOWN = 0x2000;
|
private const int SM_SHUTTINGDOWN = 0x2000;
|
||||||
|
|
||||||
public ApplicationStateManager(IContainer container, string[] startupArguments)
|
public ApplicationStateManager(IContainer container, string[] startupArguments)
|
||||||
{
|
{
|
||||||
|
_container = container;
|
||||||
|
_logger = container.Resolve<ILogger>();
|
||||||
|
|
||||||
StartupArguments = startupArguments;
|
StartupArguments = startupArguments;
|
||||||
IsElevated = new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
|
IsElevated = new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
|
||||||
|
|
||||||
@ -29,20 +35,14 @@ public class ApplicationStateManager
|
|||||||
|
|
||||||
// On Windows shutdown dispose the IOC container just so device providers get a chance to clean up
|
// On Windows shutdown dispose the IOC container just so device providers get a chance to clean up
|
||||||
if (Application.Current?.ApplicationLifetime is IControlledApplicationLifetime controlledApplicationLifetime)
|
if (Application.Current?.ApplicationLifetime is IControlledApplicationLifetime controlledApplicationLifetime)
|
||||||
controlledApplicationLifetime.Exit += (_, _) =>
|
controlledApplicationLifetime.Exit += ControlledApplicationLifetimeOnExit;
|
||||||
{
|
|
||||||
RunForcedShutdownIfEnabled();
|
|
||||||
|
|
||||||
// Dispose plugins before disposing the IOC container because plugins might access services during dispose
|
|
||||||
container.Resolve<IPluginManagementService>().Dispose();
|
|
||||||
container.Dispose();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Inform the Core about elevation status
|
// Inform the Core about elevation status
|
||||||
container.Resolve<ICoreService>().IsElevated = IsElevated;
|
container.Resolve<ICoreService>().IsElevated = IsElevated;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string[] StartupArguments { get; }
|
public string[] StartupArguments { get; }
|
||||||
|
|
||||||
public bool IsElevated { get; }
|
public bool IsElevated { get; }
|
||||||
|
|
||||||
private void UtilitiesOnRestartRequested(object? sender, RestartEventArgs e)
|
private void UtilitiesOnRestartRequested(object? sender, RestartEventArgs e)
|
||||||
@ -111,6 +111,17 @@ public class ApplicationStateManager
|
|||||||
Dispatcher.UIThread.Post(() => controlledApplicationLifetime.Shutdown());
|
Dispatcher.UIThread.Post(() => controlledApplicationLifetime.Shutdown());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ControlledApplicationLifetimeOnExit(object? sender, ControlledApplicationLifetimeExitEventArgs e)
|
||||||
|
{
|
||||||
|
_logger.Information("Application lifetime exiting, disposing container and friends");
|
||||||
|
|
||||||
|
RunForcedShutdownIfEnabled();
|
||||||
|
|
||||||
|
// Dispose plugins before disposing the IOC container because plugins might access services during dispose
|
||||||
|
_container.Resolve<IPluginManagementService>().Dispose();
|
||||||
|
_container.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
private void UtilitiesOnShutdownRequested(object? sender, EventArgs e)
|
private void UtilitiesOnShutdownRequested(object? sender, EventArgs e)
|
||||||
{
|
{
|
||||||
// Use PowerShell to kill the process after 8 sec just in case
|
// Use PowerShell to kill the process after 8 sec just in case
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Reactive;
|
using System.Reactive;
|
||||||
|
using System.Reactive.Disposables;
|
||||||
|
using System.Reactive.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
@ -28,7 +30,7 @@ public class RootViewModel : RoutableHostScreen<RoutableScreen>, IMainWindowProv
|
|||||||
private readonly ISettingsService _settingsService;
|
private readonly ISettingsService _settingsService;
|
||||||
private readonly IUpdateService _updateService;
|
private readonly IUpdateService _updateService;
|
||||||
private readonly IWindowService _windowService;
|
private readonly IWindowService _windowService;
|
||||||
private ViewModelBase? _titleBarViewModel;
|
private readonly ObservableAsPropertyHelper<ViewModelBase?> _titleBarViewModel;
|
||||||
|
|
||||||
public RootViewModel(IRouter router,
|
public RootViewModel(IRouter router,
|
||||||
ICoreService coreService,
|
ICoreService coreService,
|
||||||
@ -61,7 +63,13 @@ public class RootViewModel : RoutableHostScreen<RoutableScreen>, IMainWindowProv
|
|||||||
OpenScreen = ReactiveCommand.Create<string?>(ExecuteOpenScreen);
|
OpenScreen = ReactiveCommand.Create<string?>(ExecuteOpenScreen);
|
||||||
OpenDebugger = ReactiveCommand.CreateFromTask(ExecuteOpenDebugger);
|
OpenDebugger = ReactiveCommand.CreateFromTask(ExecuteOpenDebugger);
|
||||||
Exit = ReactiveCommand.CreateFromTask(ExecuteExit);
|
Exit = ReactiveCommand.CreateFromTask(ExecuteExit);
|
||||||
this.WhenAnyValue(vm => vm.Screen).Subscribe(UpdateTitleBarViewModel);
|
|
||||||
|
_titleBarViewModel = this.WhenAnyValue(vm => vm.Screen)
|
||||||
|
.Select(s => s as IMainScreenViewModel)
|
||||||
|
.Select(s => s?.WhenAnyValue(svm => svm.TitleBarViewModel) ?? Observable.Never<ViewModelBase>())
|
||||||
|
.Switch()
|
||||||
|
.Select(vm => vm ?? _defaultTitleBarViewModel)
|
||||||
|
.ToProperty(this, vm => vm.TitleBarViewModel);
|
||||||
|
|
||||||
Task.Run(() =>
|
Task.Run(() =>
|
||||||
{
|
{
|
||||||
@ -82,12 +90,7 @@ public class RootViewModel : RoutableHostScreen<RoutableScreen>, IMainWindowProv
|
|||||||
public ReactiveCommand<Unit, Unit> OpenDebugger { get; }
|
public ReactiveCommand<Unit, Unit> OpenDebugger { get; }
|
||||||
public ReactiveCommand<Unit, Unit> Exit { get; }
|
public ReactiveCommand<Unit, Unit> Exit { get; }
|
||||||
|
|
||||||
public ViewModelBase? TitleBarViewModel
|
public ViewModelBase? TitleBarViewModel => _titleBarViewModel.Value;
|
||||||
{
|
|
||||||
get => _titleBarViewModel;
|
|
||||||
set => RaiseAndSetIfChanged(ref _titleBarViewModel, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static PluginSetting<WindowSize?>? WindowSizeSetting { get; private set; }
|
public static PluginSetting<WindowSize?>? WindowSizeSetting { get; private set; }
|
||||||
|
|
||||||
public void GoBack()
|
public void GoBack()
|
||||||
@ -100,12 +103,6 @@ public class RootViewModel : RoutableHostScreen<RoutableScreen>, IMainWindowProv
|
|||||||
_router.GoForward();
|
_router.GoForward();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateTitleBarViewModel(RoutableScreen? viewModel)
|
|
||||||
{
|
|
||||||
IMainScreenViewModel? mainScreenViewModel = viewModel as IMainScreenViewModel;
|
|
||||||
TitleBarViewModel = mainScreenViewModel?.TitleBarViewModel ?? _defaultTitleBarViewModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CurrentMainWindowOnClosing(object? sender, EventArgs e)
|
private void CurrentMainWindowOnClosing(object? sender, EventArgs e)
|
||||||
{
|
{
|
||||||
WindowSizeSetting?.Save();
|
WindowSizeSetting?.Save();
|
||||||
|
|||||||
@ -10,30 +10,9 @@
|
|||||||
x:DataType="visualScripting:NodePickerViewModel"
|
x:DataType="visualScripting:NodePickerViewModel"
|
||||||
Width="600"
|
Width="600"
|
||||||
Height="400">
|
Height="400">
|
||||||
<UserControl.Styles>
|
|
||||||
<Style Selector="TextBox#SearchBox">
|
|
||||||
<Setter Property="VerticalAlignment" Value="Top"></Setter>
|
|
||||||
<Setter Property="InnerRightContent">
|
|
||||||
<Template>
|
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<Button Content=""
|
|
||||||
FontFamily="{StaticResource SymbolThemeFontFamily}"
|
|
||||||
Theme="{StaticResource TransparentButton}"
|
|
||||||
Command="{CompiledBinding $parent[TextBox].Clear}"
|
|
||||||
IsVisible="{CompiledBinding Text, RelativeSource={RelativeSource FindAncestor, AncestorType=TextBox}, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
|
|
||||||
<Button Content=""
|
|
||||||
FontFamily="{StaticResource SymbolThemeFontFamily}"
|
|
||||||
Theme="{StaticResource TransparentButton}"
|
|
||||||
Command="{CompiledBinding $parent[TextBox].Clear}"
|
|
||||||
IsHitTestVisible="False"/>
|
|
||||||
</StackPanel>
|
|
||||||
</Template>
|
|
||||||
</Setter>
|
|
||||||
</Style>
|
|
||||||
</UserControl.Styles>
|
|
||||||
<Border Classes="picker-container">
|
<Border Classes="picker-container">
|
||||||
<Grid RowDefinitions="Auto,*">
|
<Grid RowDefinitions="Auto,*">
|
||||||
<TextBox Name="SearchBox" Text="{CompiledBinding SearchText}" Margin="0 0 0 15" Watermark="Search"></TextBox>
|
<TextBox Name="SearchBox" Classes="search-box" Text="{CompiledBinding SearchText}" Margin="0 0 0 15" Watermark="Search nodes"></TextBox>
|
||||||
<TreeView Name="NodeTree"
|
<TreeView Name="NodeTree"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
ItemsSource="{CompiledBinding Categories}"
|
ItemsSource="{CompiledBinding Categories}"
|
||||||
|
|||||||
@ -0,0 +1,42 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
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:entries="clr-namespace:Artemis.UI.Screens.Workshop.Entries"
|
||||||
|
xmlns:system="clr-namespace:System;assembly=System.Runtime"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Artemis.UI.Screens.Workshop.Entries.EntryListInputView"
|
||||||
|
x:DataType="entries:EntryListInputViewModel">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" MaxWidth="500" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBox Grid.Column="0" Name="SearchBox" Classes="search-box" Watermark="{CompiledBinding SearchWatermark}" Text="{CompiledBinding Search}"/>
|
||||||
|
|
||||||
|
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right" Spacing="5" Margin="5 0">
|
||||||
|
<TextBlock VerticalAlignment="Center">Sort by</TextBlock>
|
||||||
|
<ComboBox Width="165" SelectedIndex="{CompiledBinding SortBy}">
|
||||||
|
<ComboBoxItem>Recently updated</ComboBoxItem>
|
||||||
|
<ComboBoxItem>Recently added</ComboBoxItem>
|
||||||
|
<ComboBoxItem>Download count</ComboBoxItem>
|
||||||
|
</ComboBox>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="5">
|
||||||
|
<TextBlock VerticalAlignment="Center">Show per page</TextBlock>
|
||||||
|
<ComboBox Width="65" SelectedItem="{CompiledBinding EntriesPerPage}">
|
||||||
|
<system:Int32>10</system:Int32>
|
||||||
|
<system:Int32>20</system:Int32>
|
||||||
|
<system:Int32>50</system:Int32>
|
||||||
|
<system:Int32>100</system:Int32>
|
||||||
|
</ComboBox>
|
||||||
|
</StackPanel>
|
||||||
|
<TextBlock Grid.Column="3" VerticalAlignment="Center" Margin="5 0 0 0" MinWidth="75" TextAlignment="Right">
|
||||||
|
<Run Text="{CompiledBinding TotalCount}"/>
|
||||||
|
<Run Text="total"/>
|
||||||
|
</TextBlock>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
</UserControl>
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Screens.Workshop.Entries;
|
||||||
|
|
||||||
|
public partial class EntryListInputView : UserControl
|
||||||
|
{
|
||||||
|
public EntryListInputView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,73 @@
|
|||||||
|
using Artemis.Core;
|
||||||
|
using Artemis.Core.Services;
|
||||||
|
using Artemis.UI.Shared;
|
||||||
|
using ReactiveUI;
|
||||||
|
|
||||||
|
namespace Artemis.UI.Screens.Workshop.Entries;
|
||||||
|
|
||||||
|
public class EntryListInputViewModel : ViewModelBase
|
||||||
|
{
|
||||||
|
private static string? _lastSearch;
|
||||||
|
private readonly PluginSetting<int> _entriesPerPage;
|
||||||
|
private readonly PluginSetting<int> _sortBy;
|
||||||
|
private string? _search;
|
||||||
|
private string _searchWatermark = "Search";
|
||||||
|
private int _totalCount;
|
||||||
|
|
||||||
|
public EntryListInputViewModel(ISettingsService settingsService)
|
||||||
|
{
|
||||||
|
_search = _lastSearch;
|
||||||
|
_entriesPerPage = settingsService.GetSetting("Workshop.EntriesPerPage", 10);
|
||||||
|
_sortBy = settingsService.GetSetting("Workshop.SortBy", 10);
|
||||||
|
_entriesPerPage.AutoSave = true;
|
||||||
|
_sortBy.AutoSave = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string SearchWatermark
|
||||||
|
{
|
||||||
|
get => _searchWatermark;
|
||||||
|
set => RaiseAndSetIfChanged(ref _searchWatermark, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string? Search
|
||||||
|
{
|
||||||
|
get => _search;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
RaiseAndSetIfChanged(ref _search, value);
|
||||||
|
_lastSearch = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int EntriesPerPage
|
||||||
|
{
|
||||||
|
get => _entriesPerPage.Value;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_entriesPerPage.Value = value;
|
||||||
|
this.RaisePropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int SortBy
|
||||||
|
{
|
||||||
|
get => _sortBy.Value;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_sortBy.Value = value;
|
||||||
|
this.RaisePropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int TotalCount
|
||||||
|
{
|
||||||
|
get => _totalCount;
|
||||||
|
set => RaiseAndSetIfChanged(ref _totalCount, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearLastSearch()
|
||||||
|
{
|
||||||
|
_lastSearch = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Reactive.Disposables;
|
using System.Reactive.Disposables;
|
||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
@ -6,7 +7,6 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Artemis.UI.Screens.Workshop.Categories;
|
using Artemis.UI.Screens.Workshop.Categories;
|
||||||
using Artemis.UI.Screens.Workshop.Parameters;
|
using Artemis.UI.Screens.Workshop.Parameters;
|
||||||
using Artemis.UI.Shared;
|
|
||||||
using Artemis.UI.Shared.Routing;
|
using Artemis.UI.Shared.Routing;
|
||||||
using Artemis.UI.Shared.Services;
|
using Artemis.UI.Shared.Services;
|
||||||
using Artemis.UI.Shared.Services.Builders;
|
using Artemis.UI.Shared.Services.Builders;
|
||||||
@ -20,26 +20,33 @@ namespace Artemis.UI.Screens.Workshop.Entries;
|
|||||||
|
|
||||||
public abstract class EntryListViewModel : RoutableScreen<WorkshopListParameters>
|
public abstract class EntryListViewModel : RoutableScreen<WorkshopListParameters>
|
||||||
{
|
{
|
||||||
private readonly INotificationService _notificationService;
|
|
||||||
private readonly IWorkshopClient _workshopClient;
|
|
||||||
private readonly ObservableAsPropertyHelper<bool> _showPagination;
|
|
||||||
private readonly ObservableAsPropertyHelper<bool> _isLoading;
|
|
||||||
private readonly SourceList<IGetEntries_Entries_Items> _entries = new();
|
private readonly SourceList<IGetEntries_Entries_Items> _entries = new();
|
||||||
|
private readonly ObservableAsPropertyHelper<bool> _isLoading;
|
||||||
|
private readonly INotificationService _notificationService;
|
||||||
|
private readonly string _route;
|
||||||
|
private readonly ObservableAsPropertyHelper<bool> _showPagination;
|
||||||
|
private readonly IWorkshopClient _workshopClient;
|
||||||
|
private int _loadedPage = -1;
|
||||||
|
|
||||||
private int _page;
|
private int _page;
|
||||||
private int _loadedPage = -1;
|
|
||||||
private int _totalPages = 1;
|
private int _totalPages = 1;
|
||||||
private int _entriesPerPage = 10;
|
|
||||||
|
|
||||||
protected EntryListViewModel(IWorkshopClient workshopClient, IRouter router, CategoriesViewModel categoriesViewModel, INotificationService notificationService,
|
protected EntryListViewModel(string route,
|
||||||
|
IWorkshopClient workshopClient,
|
||||||
|
IRouter router,
|
||||||
|
CategoriesViewModel categoriesViewModel,
|
||||||
|
EntryListInputViewModel entryListInputViewModel,
|
||||||
|
INotificationService notificationService,
|
||||||
Func<IGetEntries_Entries_Items, EntryListItemViewModel> getEntryListViewModel)
|
Func<IGetEntries_Entries_Items, EntryListItemViewModel> getEntryListViewModel)
|
||||||
{
|
{
|
||||||
|
_route = route;
|
||||||
_workshopClient = workshopClient;
|
_workshopClient = workshopClient;
|
||||||
_notificationService = notificationService;
|
_notificationService = notificationService;
|
||||||
_showPagination = this.WhenAnyValue(vm => vm.TotalPages).Select(t => t > 1).ToProperty(this, vm => vm.ShowPagination);
|
_showPagination = this.WhenAnyValue(vm => vm.TotalPages).Select(t => t > 1).ToProperty(this, vm => vm.ShowPagination);
|
||||||
_isLoading = this.WhenAnyValue(vm => vm.Page, vm => vm.LoadedPage, (p, c) => p != c).ToProperty(this, vm => vm.IsLoading);
|
_isLoading = this.WhenAnyValue(vm => vm.Page, vm => vm.LoadedPage, (p, c) => p != c).ToProperty(this, vm => vm.IsLoading);
|
||||||
|
|
||||||
CategoriesViewModel = categoriesViewModel;
|
CategoriesViewModel = categoriesViewModel;
|
||||||
|
InputViewModel = entryListInputViewModel;
|
||||||
|
|
||||||
_entries.Connect()
|
_entries.Connect()
|
||||||
.ObserveOn(new AvaloniaSynchronizationContext(DispatcherPriority.SystemIdle))
|
.ObserveOn(new AvaloniaSynchronizationContext(DispatcherPriority.SystemIdle))
|
||||||
@ -49,26 +56,22 @@ public abstract class EntryListViewModel : RoutableScreen<WorkshopListParameters
|
|||||||
Entries = entries;
|
Entries = entries;
|
||||||
|
|
||||||
// Respond to page changes
|
// Respond to page changes
|
||||||
this.WhenAnyValue(vm => vm.Page).Skip(1).Subscribe(p => Task.Run(() => router.Navigate(GetPagePath(p))));
|
this.WhenAnyValue(vm => vm.Page).Skip(1).Subscribe(p => Task.Run(() => router.Navigate($"{_route}/{p}")));
|
||||||
|
|
||||||
// Respond to filter changes
|
this.WhenActivated(d =>
|
||||||
this.WhenActivated(d => CategoriesViewModel.WhenAnyValue(vm => vm.CategoryFilters).Skip(1).Subscribe(_ =>
|
|
||||||
{
|
{
|
||||||
// Reset to page one, will trigger a query
|
// Respond to filter query input changes
|
||||||
if (Page != 1)
|
InputViewModel.WhenAnyValue(vm => vm.Search).Skip(1).Throttle(TimeSpan.FromMilliseconds(200)).Subscribe(_ => RefreshToStart()).DisposeWith(d);
|
||||||
Page = 1;
|
InputViewModel.WhenAnyValue(vm => vm.SortBy, vm => vm.EntriesPerPage).Skip(1).Subscribe(_ => RefreshToStart()).DisposeWith(d);
|
||||||
// If already at page one, force a query
|
CategoriesViewModel.WhenAnyValue(vm => vm.CategoryFilters).Skip(1).Subscribe(_ => RefreshToStart()).DisposeWith(d);
|
||||||
else
|
});
|
||||||
Task.Run(() => Query(CancellationToken.None));
|
|
||||||
}).DisposeWith(d));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract string GetPagePath(int page);
|
|
||||||
|
|
||||||
public bool ShowPagination => _showPagination.Value;
|
public bool ShowPagination => _showPagination.Value;
|
||||||
public bool IsLoading => _isLoading.Value;
|
public bool IsLoading => _isLoading.Value;
|
||||||
|
|
||||||
public CategoriesViewModel CategoriesViewModel { get; }
|
public CategoriesViewModel CategoriesViewModel { get; }
|
||||||
|
public EntryListInputViewModel InputViewModel { get; }
|
||||||
|
|
||||||
public ReadOnlyObservableCollection<EntryListItemViewModel> Entries { get; }
|
public ReadOnlyObservableCollection<EntryListItemViewModel> Entries { get; }
|
||||||
|
|
||||||
@ -84,18 +87,13 @@ public abstract class EntryListViewModel : RoutableScreen<WorkshopListParameters
|
|||||||
set => RaiseAndSetIfChanged(ref _loadedPage, value);
|
set => RaiseAndSetIfChanged(ref _loadedPage, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public int TotalPages
|
public int TotalPages
|
||||||
{
|
{
|
||||||
get => _totalPages;
|
get => _totalPages;
|
||||||
set => RaiseAndSetIfChanged(ref _totalPages, value);
|
set => RaiseAndSetIfChanged(ref _totalPages, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int EntriesPerPage
|
|
||||||
{
|
|
||||||
get => _entriesPerPage;
|
|
||||||
set => RaiseAndSetIfChanged(ref _entriesPerPage, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async 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);
|
Page = Math.Max(1, parameters.Page);
|
||||||
@ -105,17 +103,70 @@ public abstract class EntryListViewModel : RoutableScreen<WorkshopListParameters
|
|||||||
await Query(cancellationToken);
|
await Query(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected virtual EntryFilterInput GetFilter()
|
||||||
|
{
|
||||||
|
return new EntryFilterInput {And = CategoriesViewModel.CategoryFilters};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual IReadOnlyList<EntrySortInput> GetSort()
|
||||||
|
{
|
||||||
|
// Sort by created at
|
||||||
|
if (InputViewModel.SortBy == 1)
|
||||||
|
return new[] {new EntrySortInput {CreatedAt = SortEnumType.Desc}};
|
||||||
|
|
||||||
|
// Sort by downloads
|
||||||
|
if (InputViewModel.SortBy == 2)
|
||||||
|
return new[] {new EntrySortInput {Downloads = SortEnumType.Desc}};
|
||||||
|
|
||||||
|
|
||||||
|
// Sort by latest release, then by created at
|
||||||
|
return new[]
|
||||||
|
{
|
||||||
|
new EntrySortInput {LatestRelease = new ReleaseSortInput {CreatedAt = SortEnumType.Desc}},
|
||||||
|
new EntrySortInput {CreatedAt = SortEnumType.Desc}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshToStart()
|
||||||
|
{
|
||||||
|
// Reset to page one, will trigger a query
|
||||||
|
if (Page != 1)
|
||||||
|
Page = 1;
|
||||||
|
// If already at page one, force a query
|
||||||
|
else
|
||||||
|
Task.Run(() => Query(CancellationToken.None));
|
||||||
|
}
|
||||||
|
|
||||||
private async Task Query(CancellationToken cancellationToken)
|
private async Task Query(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
string? search = string.IsNullOrWhiteSpace(InputViewModel.Search) ? null : InputViewModel.Search;
|
||||||
EntryFilterInput filter = GetFilter();
|
EntryFilterInput filter = GetFilter();
|
||||||
IOperationResult<IGetEntriesResult> entries = await _workshopClient.GetEntries.ExecuteAsync(filter, EntriesPerPage * (Page - 1), EntriesPerPage, cancellationToken);
|
IReadOnlyList<EntrySortInput> sort = GetSort();
|
||||||
|
IOperationResult<IGetEntriesResult> entries = await _workshopClient.GetEntries.ExecuteAsync(
|
||||||
|
search,
|
||||||
|
filter,
|
||||||
|
InputViewModel.EntriesPerPage * (Page - 1),
|
||||||
|
InputViewModel.EntriesPerPage,
|
||||||
|
sort,
|
||||||
|
cancellationToken
|
||||||
|
);
|
||||||
entries.EnsureNoErrors();
|
entries.EnsureNoErrors();
|
||||||
|
|
||||||
if (entries.Data?.Entries?.Items != null)
|
if (entries.Data?.Entries?.Items != null)
|
||||||
{
|
{
|
||||||
TotalPages = (int) Math.Ceiling(entries.Data.Entries.TotalCount / (double) EntriesPerPage);
|
TotalPages = (int) Math.Ceiling(entries.Data.Entries.TotalCount / (double) InputViewModel.EntriesPerPage);
|
||||||
|
InputViewModel.TotalCount = entries.Data.Entries.TotalCount;
|
||||||
_entries.Edit(e =>
|
_entries.Edit(e =>
|
||||||
{
|
{
|
||||||
e.Clear();
|
e.Clear();
|
||||||
@ -123,8 +174,10 @@ public abstract class EntryListViewModel : RoutableScreen<WorkshopListParameters
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
TotalPages = 1;
|
TotalPages = 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_notificationService.CreateNotification()
|
_notificationService.CreateNotification()
|
||||||
@ -138,9 +191,4 @@ public abstract class EntryListViewModel : RoutableScreen<WorkshopListParameters
|
|||||||
LoadedPage = Page;
|
LoadedPage = Page;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual EntryFilterInput GetFilter()
|
|
||||||
{
|
|
||||||
return new EntryFilterInput {And = CategoriesViewModel.CategoryFilters};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -2,14 +2,21 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:layout="clr-namespace:Artemis.UI.Screens.Workshop.Layout"
|
|
||||||
xmlns:pagination="clr-namespace:Artemis.UI.Shared.Pagination;assembly=Artemis.UI.Shared"
|
xmlns:pagination="clr-namespace:Artemis.UI.Shared.Pagination;assembly=Artemis.UI.Shared"
|
||||||
xmlns:tabs="clr-namespace:Artemis.UI.Screens.Workshop.Entries.Tabs"
|
xmlns:tabs="clr-namespace:Artemis.UI.Screens.Workshop.Entries.Tabs"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Artemis.UI.Screens.Workshop.Entries.Tabs.LayoutListView"
|
x:Class="Artemis.UI.Screens.Workshop.Entries.Tabs.LayoutListView"
|
||||||
x:DataType="tabs:LayoutListViewModel">
|
x:DataType="tabs:LayoutListViewModel">
|
||||||
<Grid ColumnDefinitions="300,*" RowDefinitions="*,Auto">
|
<UserControl.Styles>
|
||||||
<StackPanel Grid.Column="0" Grid.RowSpan="2" Margin="0 0 10 0" VerticalAlignment="Top">
|
<Styles>
|
||||||
|
<Style Selector="StackPanel.empty-state > TextBlock">
|
||||||
|
<Setter Property="TextAlignment" Value="Center"></Setter>
|
||||||
|
<Setter Property="TextWrapping" Value="Wrap"></Setter>
|
||||||
|
</Style>
|
||||||
|
</Styles>
|
||||||
|
</UserControl.Styles>
|
||||||
|
<Grid ColumnDefinitions="300,*" RowDefinitions="Auto,*,Auto">
|
||||||
|
<StackPanel Grid.Column="0" Grid.RowSpan="3" Margin="0 0 10 0" VerticalAlignment="Top">
|
||||||
<Border Classes="card" VerticalAlignment="Stretch">
|
<Border Classes="card" VerticalAlignment="Stretch">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}">Categories</TextBlock>
|
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}">Categories</TextBlock>
|
||||||
@ -20,18 +27,35 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<ProgressBar Grid.Column="1" Grid.Row="0" VerticalAlignment="Top" Margin="0 0 20 0" IsVisible="{CompiledBinding IsLoading}" IsIndeterminate="True" />
|
<ProgressBar Grid.Column="1" Grid.Row="0" VerticalAlignment="Top" Margin="0 0 20 0" IsVisible="{CompiledBinding IsLoading}" IsIndeterminate="True" />
|
||||||
<ScrollViewer Grid.Column="1" Grid.Row="0">
|
<ContentControl Grid.Column="1" Grid.Row="0" Margin="0 0 20 8" Content="{CompiledBinding InputViewModel}"/>
|
||||||
<ItemsRepeater ItemsSource="{CompiledBinding Entries}" Margin="0 0 20 0">
|
|
||||||
<ItemsRepeater.ItemTemplate>
|
<ScrollViewer Grid.Column="1" Grid.Row="1">
|
||||||
|
<ItemsControl ItemsSource="{CompiledBinding Entries}" Margin="0 0 20 0">
|
||||||
|
<ItemsControl.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<VirtualizingStackPanel />
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ItemsControl.ItemsPanel>
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<ContentControl Content="{CompiledBinding}" Margin="0 0 0 5"></ContentControl>
|
<ContentControl Content="{CompiledBinding}" Margin="0 0 0 5"></ContentControl>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ItemsRepeater.ItemTemplate>
|
</ItemsControl.ItemTemplate>
|
||||||
</ItemsRepeater>
|
</ItemsControl>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
|
|
||||||
|
<Panel Grid.Column="1" Grid.Row="1" IsVisible="{CompiledBinding !IsLoading}">
|
||||||
|
<StackPanel IsVisible="{CompiledBinding !Entries.Count}" Margin="0 50 0 0" Classes="empty-state">
|
||||||
|
<TextBlock Theme="{StaticResource TitleTextBlockStyle}">Looks like your current filters gave no results</TextBlock>
|
||||||
|
<TextBlock>
|
||||||
|
<Run>Modify or clear your filters to view other device layouts</Run>
|
||||||
|
</TextBlock>
|
||||||
|
<Lottie Path="/Assets/Animations/empty.json" RepeatCount="1" Width="350" Height="350"></Lottie>
|
||||||
|
</StackPanel>
|
||||||
|
</Panel>
|
||||||
|
|
||||||
<pagination:Pagination Grid.Column="1"
|
<pagination:Pagination Grid.Column="1"
|
||||||
Grid.Row="1"
|
Grid.Row="2"
|
||||||
Margin="0 20 0 10"
|
Margin="0 20 0 10"
|
||||||
IsVisible="{CompiledBinding ShowPagination}"
|
IsVisible="{CompiledBinding ShowPagination}"
|
||||||
Value="{CompiledBinding Page}"
|
Value="{CompiledBinding Page}"
|
||||||
|
|||||||
@ -8,36 +8,26 @@ namespace Artemis.UI.Screens.Workshop.Entries.Tabs;
|
|||||||
|
|
||||||
public class LayoutListViewModel : EntryListViewModel
|
public class LayoutListViewModel : EntryListViewModel
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
|
||||||
public LayoutListViewModel(IWorkshopClient workshopClient,
|
public LayoutListViewModel(IWorkshopClient workshopClient,
|
||||||
IRouter router,
|
IRouter router,
|
||||||
CategoriesViewModel categoriesViewModel,
|
CategoriesViewModel categoriesViewModel,
|
||||||
|
EntryListInputViewModel entryListInputViewModel,
|
||||||
INotificationService notificationService,
|
INotificationService notificationService,
|
||||||
Func<IGetEntries_Entries_Items, EntryListItemViewModel> getEntryListViewModel)
|
Func<IGetEntries_Entries_Items, EntryListItemViewModel> getEntryListViewModel)
|
||||||
: base(workshopClient, router, categoriesViewModel, notificationService, getEntryListViewModel)
|
: base("workshop/entries/layout", workshopClient, router, categoriesViewModel, entryListInputViewModel, notificationService, getEntryListViewModel)
|
||||||
{
|
{
|
||||||
|
entryListInputViewModel.SearchWatermark = "Search layouts";
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Overrides of EntryListBaseViewModel
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override string GetPagePath(int page)
|
|
||||||
{
|
|
||||||
return $"workshop/entries/layouts/{page}";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override EntryFilterInput GetFilter()
|
protected override EntryFilterInput GetFilter()
|
||||||
{
|
{
|
||||||
return new EntryFilterInput
|
return new EntryFilterInput
|
||||||
{
|
{
|
||||||
And = new[]
|
And = new[]
|
||||||
{
|
{
|
||||||
new EntryFilterInput {EntryType = new EntryTypeOperationFilterInput {Eq = WebClient.Workshop.EntryType.Layout}},
|
new EntryFilterInput {EntryType = new EntryTypeOperationFilterInput {Eq = EntryType.Layout}},
|
||||||
base.GetFilter()
|
base.GetFilter()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
@ -2,14 +2,21 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:profile="clr-namespace:Artemis.UI.Screens.Workshop.Profile"
|
|
||||||
xmlns:pagination="clr-namespace:Artemis.UI.Shared.Pagination;assembly=Artemis.UI.Shared"
|
xmlns:pagination="clr-namespace:Artemis.UI.Shared.Pagination;assembly=Artemis.UI.Shared"
|
||||||
xmlns:tabs="clr-namespace:Artemis.UI.Screens.Workshop.Entries.Tabs"
|
xmlns:tabs="clr-namespace:Artemis.UI.Screens.Workshop.Entries.Tabs"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="450"
|
||||||
x:Class="Artemis.UI.Screens.Workshop.Entries.Tabs.ProfileListView"
|
x:Class="Artemis.UI.Screens.Workshop.Entries.Tabs.ProfileListView"
|
||||||
x:DataType="tabs:ProfileListViewModel">
|
x:DataType="tabs:ProfileListViewModel">
|
||||||
<Grid ColumnDefinitions="300,*" RowDefinitions="*,Auto">
|
<UserControl.Styles>
|
||||||
<StackPanel Grid.Column="0" Grid.RowSpan="2" Margin="0 0 10 0" VerticalAlignment="Top">
|
<Styles>
|
||||||
|
<Style Selector="StackPanel.empty-state > TextBlock">
|
||||||
|
<Setter Property="TextAlignment" Value="Center"></Setter>
|
||||||
|
<Setter Property="TextWrapping" Value="Wrap"></Setter>
|
||||||
|
</Style>
|
||||||
|
</Styles>
|
||||||
|
</UserControl.Styles>
|
||||||
|
<Grid ColumnDefinitions="300,*" RowDefinitions="Auto,*,Auto">
|
||||||
|
<StackPanel Grid.Column="0" Grid.RowSpan="3" Margin="0 0 10 0" VerticalAlignment="Top">
|
||||||
<Border Classes="card" VerticalAlignment="Stretch">
|
<Border Classes="card" VerticalAlignment="Stretch">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}">Categories</TextBlock>
|
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}">Categories</TextBlock>
|
||||||
@ -20,19 +27,35 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<ProgressBar Grid.Column="1" Grid.Row="0" VerticalAlignment="Top" Margin="0 0 20 0" IsVisible="{CompiledBinding IsLoading}" IsIndeterminate="True" />
|
<ProgressBar Grid.Column="1" Grid.Row="0" VerticalAlignment="Top" Margin="0 0 20 0" IsVisible="{CompiledBinding IsLoading}" IsIndeterminate="True" />
|
||||||
|
<ContentControl Grid.Column="1" Grid.Row="0" Margin="0 0 20 8" Content="{CompiledBinding InputViewModel}"/>
|
||||||
|
|
||||||
<ScrollViewer Grid.Column="1" Grid.Row="0">
|
<ScrollViewer Grid.Column="1" Grid.Row="1">
|
||||||
<ItemsRepeater ItemsSource="{CompiledBinding Entries}" Margin="0 0 20 0">
|
<ItemsControl ItemsSource="{CompiledBinding Entries}" Margin="0 0 20 0">
|
||||||
<ItemsRepeater.ItemTemplate>
|
<ItemsControl.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<VirtualizingStackPanel />
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ItemsControl.ItemsPanel>
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<ContentControl Content="{CompiledBinding}" Margin="0 0 0 5"></ContentControl>
|
<ContentControl Content="{CompiledBinding}" Margin="0 0 0 5"></ContentControl>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ItemsRepeater.ItemTemplate>
|
</ItemsControl.ItemTemplate>
|
||||||
</ItemsRepeater>
|
</ItemsControl>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
|
|
||||||
|
<Panel Grid.Column="1" Grid.Row="1" IsVisible="{CompiledBinding !IsLoading}">
|
||||||
|
<StackPanel IsVisible="{CompiledBinding !Entries.Count}" Margin="0 50 0 0" Classes="empty-state">
|
||||||
|
<TextBlock Theme="{StaticResource TitleTextBlockStyle}">Looks like your current filters gave no results</TextBlock>
|
||||||
|
<TextBlock>
|
||||||
|
<Run>Modify or clear your filters to view some awesome profiles</Run>
|
||||||
|
</TextBlock>
|
||||||
|
<Lottie Path="/Assets/Animations/empty.json" RepeatCount="1" Width="350" Height="350"></Lottie>
|
||||||
|
</StackPanel>
|
||||||
|
</Panel>
|
||||||
|
|
||||||
<pagination:Pagination Grid.Column="1"
|
<pagination:Pagination Grid.Column="1"
|
||||||
Grid.Row="1"
|
Grid.Row="2"
|
||||||
Margin="0 20 0 10"
|
Margin="0 20 0 10"
|
||||||
IsVisible="{CompiledBinding ShowPagination}"
|
IsVisible="{CompiledBinding ShowPagination}"
|
||||||
Value="{CompiledBinding Page}"
|
Value="{CompiledBinding Page}"
|
||||||
|
|||||||
@ -8,36 +8,26 @@ namespace Artemis.UI.Screens.Workshop.Entries.Tabs;
|
|||||||
|
|
||||||
public class ProfileListViewModel : EntryListViewModel
|
public class ProfileListViewModel : EntryListViewModel
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
|
||||||
public ProfileListViewModel(IWorkshopClient workshopClient,
|
public ProfileListViewModel(IWorkshopClient workshopClient,
|
||||||
IRouter router,
|
IRouter router,
|
||||||
CategoriesViewModel categoriesViewModel,
|
CategoriesViewModel categoriesViewModel,
|
||||||
|
EntryListInputViewModel entryListInputViewModel,
|
||||||
INotificationService notificationService,
|
INotificationService notificationService,
|
||||||
Func<IGetEntries_Entries_Items, EntryListItemViewModel> getEntryListViewModel)
|
Func<IGetEntries_Entries_Items, EntryListItemViewModel> getEntryListViewModel)
|
||||||
: base(workshopClient, router, categoriesViewModel, notificationService, getEntryListViewModel)
|
: base("workshop/entries/profiles", workshopClient, router, categoriesViewModel, entryListInputViewModel, notificationService, getEntryListViewModel)
|
||||||
{
|
{
|
||||||
|
entryListInputViewModel.SearchWatermark = "Search profiles";
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Overrides of EntryListBaseViewModel
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override string GetPagePath(int page)
|
|
||||||
{
|
|
||||||
return $"workshop/entries/profiles/{page}";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override EntryFilterInput GetFilter()
|
protected override EntryFilterInput GetFilter()
|
||||||
{
|
{
|
||||||
return new EntryFilterInput
|
return new EntryFilterInput
|
||||||
{
|
{
|
||||||
And = new[]
|
And = new[]
|
||||||
{
|
{
|
||||||
new EntryFilterInput {EntryType = new EntryTypeOperationFilterInput {Eq = WebClient.Workshop.EntryType.Profile}},
|
new EntryFilterInput {EntryType = new EntryTypeOperationFilterInput {Eq = EntryType.Profile}},
|
||||||
base.GetFilter()
|
base.GetFilter()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
@ -3,7 +3,6 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:search="clr-namespace:Artemis.UI.Screens.Workshop.Search"
|
xmlns:search="clr-namespace:Artemis.UI.Screens.Workshop.Search"
|
||||||
xmlns:workshop="clr-namespace:Artemis.WebClient.Workshop;assembly=Artemis.WebClient.Workshop"
|
|
||||||
xmlns:windowing="clr-namespace:FluentAvalonia.UI.Windowing;assembly=FluentAvalonia"
|
xmlns:windowing="clr-namespace:FluentAvalonia.UI.Windowing;assembly=FluentAvalonia"
|
||||||
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
using Artemis.UI.Screens.Workshop.Home;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Artemis.UI.Screens.Workshop.Home;
|
||||||
using Artemis.UI.Screens.Workshop.Search;
|
using Artemis.UI.Screens.Workshop.Search;
|
||||||
using Artemis.UI.Shared;
|
using Artemis.UI.Shared;
|
||||||
using Artemis.UI.Shared.Routing;
|
using Artemis.UI.Shared.Routing;
|
||||||
@ -7,12 +9,27 @@ namespace Artemis.UI.Screens.Workshop;
|
|||||||
|
|
||||||
public class WorkshopViewModel : RoutableHostScreen<RoutableScreen>, IMainScreenViewModel
|
public class WorkshopViewModel : RoutableHostScreen<RoutableScreen>, IMainScreenViewModel
|
||||||
{
|
{
|
||||||
|
private readonly SearchViewModel _searchViewModel;
|
||||||
|
private ViewModelBase? _titleBarViewModel;
|
||||||
|
|
||||||
public WorkshopViewModel(SearchViewModel searchViewModel, WorkshopHomeViewModel homeViewModel)
|
public WorkshopViewModel(SearchViewModel searchViewModel, WorkshopHomeViewModel homeViewModel)
|
||||||
{
|
{
|
||||||
TitleBarViewModel = searchViewModel;
|
_searchViewModel = searchViewModel;
|
||||||
HomeViewModel = homeViewModel;
|
HomeViewModel = homeViewModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ViewModelBase TitleBarViewModel { get; }
|
|
||||||
public WorkshopHomeViewModel HomeViewModel { get; }
|
public WorkshopHomeViewModel HomeViewModel { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override Task OnNavigating(NavigationArguments args, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
TitleBarViewModel = args.Path == "workshop" ? _searchViewModel : null;
|
||||||
|
return base.OnNavigating(args, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ViewModelBase? TitleBarViewModel
|
||||||
|
{
|
||||||
|
get => _titleBarViewModel;
|
||||||
|
set => RaiseAndSetIfChanged(ref _titleBarViewModel, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -1,5 +1,5 @@
|
|||||||
query GetEntries($filter: EntryFilterInput $skip: Int $take: Int) {
|
query GetEntries($search: String $filter: EntryFilterInput $skip: Int $take: Int $order: [EntrySortInput!]) {
|
||||||
entries(where: $filter skip: $skip take: $take, order: {createdAt: DESC}) {
|
entries(search: $search where: $filter skip: $skip take: $take, order: $order) {
|
||||||
totalCount
|
totalCount
|
||||||
items {
|
items {
|
||||||
id
|
id
|
||||||
|
|||||||
@ -61,7 +61,7 @@ type Mutation {
|
|||||||
|
|
||||||
type Query {
|
type Query {
|
||||||
categories(order: [CategorySortInput!], where: CategoryFilterInput): [Category!]!
|
categories(order: [CategorySortInput!], where: CategoryFilterInput): [Category!]!
|
||||||
entries(order: [EntrySortInput!], skip: Int, take: Int, where: EntryFilterInput): EntriesCollectionSegment
|
entries(order: [EntrySortInput!], search: String, skip: Int, take: Int, where: EntryFilterInput): EntriesCollectionSegment
|
||||||
entry(id: Long!): Entry
|
entry(id: Long!): Entry
|
||||||
searchEntries(input: String!, order: [EntrySortInput!], type: EntryType, where: EntryFilterInput): [Entry!]!
|
searchEntries(input: String!, order: [EntrySortInput!], type: EntryType, where: EntryFilterInput): [Entry!]!
|
||||||
submittedEntries(order: [EntrySortInput!], where: EntryFilterInput): [Entry!]!
|
submittedEntries(order: [EntrySortInput!], where: EntryFilterInput): [Entry!]!
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user