mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Show multiple releases and improve workshop routing
This commit is contained in:
parent
da3d47d7b8
commit
9c04932afa
@ -44,5 +44,22 @@
|
|||||||
<DependentUpon>DeviceSelectionDialogView.axaml</DependentUpon>
|
<DependentUpon>DeviceSelectionDialogView.axaml</DependentUpon>
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Update="Screens\Workshop\Layout\LayoutListView.axaml.cs">
|
||||||
|
<DependentUpon>LayoutListView.axaml</DependentUpon>
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Update="Screens\Workshop\Plugins\PluginListView.axaml.cs">
|
||||||
|
<DependentUpon>LayoutListView.axaml</DependentUpon>
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Update="Screens\Workshop\Profile\ProfileListView.axaml.cs">
|
||||||
|
<DependentUpon>LayoutListView.axaml</DependentUpon>
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<UpToDateCheckInput Remove="Screens\Workshop\Entries\Tabs\PluginListView.axaml" />
|
||||||
|
<UpToDateCheckInput Remove="Screens\Workshop\Entries\Tabs\ProfileListView.axaml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@ -7,11 +7,11 @@ using Artemis.UI.Screens.Settings.Updating;
|
|||||||
using Artemis.UI.Screens.SurfaceEditor;
|
using Artemis.UI.Screens.SurfaceEditor;
|
||||||
using Artemis.UI.Screens.Workshop;
|
using Artemis.UI.Screens.Workshop;
|
||||||
using Artemis.UI.Screens.Workshop.Entries;
|
using Artemis.UI.Screens.Workshop.Entries;
|
||||||
using Artemis.UI.Screens.Workshop.Entries.Tabs;
|
|
||||||
using Artemis.UI.Screens.Workshop.Home;
|
using Artemis.UI.Screens.Workshop.Home;
|
||||||
using Artemis.UI.Screens.Workshop.Layout;
|
using Artemis.UI.Screens.Workshop.Layout;
|
||||||
using Artemis.UI.Screens.Workshop.Library;
|
using Artemis.UI.Screens.Workshop.Library;
|
||||||
using Artemis.UI.Screens.Workshop.Library.Tabs;
|
using Artemis.UI.Screens.Workshop.Library.Tabs;
|
||||||
|
using Artemis.UI.Screens.Workshop.Plugins;
|
||||||
using Artemis.UI.Screens.Workshop.Profile;
|
using Artemis.UI.Screens.Workshop.Profile;
|
||||||
using Artemis.UI.Shared.Routing;
|
using Artemis.UI.Shared.Routing;
|
||||||
using PluginDetailsViewModel = Artemis.UI.Screens.Workshop.Plugins.PluginDetailsViewModel;
|
using PluginDetailsViewModel = Artemis.UI.Screens.Workshop.Plugins.PluginDetailsViewModel;
|
||||||
|
|||||||
@ -19,7 +19,7 @@ public partial class RootView : ReactiveUserControl<RootViewModel>
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Invoke(() => RootFrame.NavigateFromObject(viewModel));
|
RootFrame.NavigateFromObject(viewModel);
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -18,7 +18,7 @@ public partial class SettingsView : ReactiveUserControl<SettingsViewModel>
|
|||||||
|
|
||||||
private void Navigate(ViewModelBase viewModel)
|
private void Navigate(ViewModelBase viewModel)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Invoke(() => TabFrame.NavigateFromObject(viewModel));
|
TabFrame.NavigateFromObject(viewModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void NavigationView_OnBackRequested(object? sender, NavigationViewBackRequestedEventArgs e)
|
private void NavigationView_OnBackRequested(object? sender, NavigationViewBackRequestedEventArgs e)
|
||||||
|
|||||||
@ -17,17 +17,13 @@ public partial class ReleasesTabView : ReactiveUserControl<ReleasesTabViewModel>
|
|||||||
|
|
||||||
private void Navigate(ViewModelBase viewModel)
|
private void Navigate(ViewModelBase viewModel)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Invoke(() =>
|
try
|
||||||
{
|
{
|
||||||
try
|
ReleaseFrame.NavigateFromObject(viewModel);
|
||||||
{
|
}
|
||||||
ReleaseFrame.NavigateFromObject(viewModel);
|
catch (Exception)
|
||||||
}
|
{
|
||||||
catch (Exception)
|
// ignored
|
||||||
{
|
}
|
||||||
// ignored
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.UI.Shared;
|
using Artemis.UI.Shared;
|
||||||
@ -12,12 +13,12 @@ public class EntryInfoViewModel : ViewModelBase
|
|||||||
private readonly INotificationService _notificationService;
|
private readonly INotificationService _notificationService;
|
||||||
public IEntryDetails Entry { get; }
|
public IEntryDetails Entry { get; }
|
||||||
public DateTimeOffset? UpdatedAt { get; }
|
public DateTimeOffset? UpdatedAt { get; }
|
||||||
|
|
||||||
public EntryInfoViewModel(IEntryDetails entry, INotificationService notificationService)
|
public EntryInfoViewModel(IEntryDetails entry, INotificationService notificationService)
|
||||||
{
|
{
|
||||||
_notificationService = notificationService;
|
_notificationService = notificationService;
|
||||||
Entry = entry;
|
Entry = entry;
|
||||||
UpdatedAt = Entry.LatestRelease?.CreatedAt ?? Entry.CreatedAt;
|
UpdatedAt = Entry.Releases.Any() ? Entry.Releases.Max(r => r.CreatedAt) : Entry.CreatedAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CopyShareLink()
|
public async Task CopyShareLink()
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
xmlns:converters="clr-namespace:Artemis.UI.Converters"
|
xmlns:converters="clr-namespace:Artemis.UI.Converters"
|
||||||
xmlns:sharedConverters="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared"
|
xmlns:sharedConverters="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared"
|
||||||
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
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"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Artemis.UI.Screens.Workshop.Entries.Details.EntryReleasesView"
|
x:Class="Artemis.UI.Screens.Workshop.Entries.Details.EntryReleasesView"
|
||||||
x:DataType="details:EntryReleasesViewModel">
|
x:DataType="details:EntryReleasesViewModel">
|
||||||
@ -14,39 +15,74 @@
|
|||||||
<sharedConverters:BytesToStringConverter x:Key="BytesToStringConverter" />
|
<sharedConverters:BytesToStringConverter x:Key="BytesToStringConverter" />
|
||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}">Latest release</TextBlock>
|
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}">Releases</TextBlock>
|
||||||
<Border Classes="card-separator" />
|
<Border Classes="card-separator" />
|
||||||
<Button HorizontalAlignment="Stretch"
|
|
||||||
|
<Button Margin="0 0 0 5"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
HorizontalContentAlignment="Stretch"
|
HorizontalContentAlignment="Stretch"
|
||||||
Command="{CompiledBinding DownloadLatestRelease}">
|
Command="{CompiledBinding NavigateToRelease}"
|
||||||
|
CommandParameter="{CompiledBinding LatestRelease}">
|
||||||
<Grid ColumnDefinitions="Auto,*">
|
<Grid ColumnDefinitions="Auto,*">
|
||||||
<!-- Icon -->
|
<!-- Icon -->
|
||||||
<Border Grid.Column="0"
|
<avalonia:MaterialIcon Grid.Column="0"
|
||||||
CornerRadius="4"
|
Margin="0 6"
|
||||||
Background="{StaticResource SystemAccentColor}"
|
Width="50"
|
||||||
VerticalAlignment="Center"
|
Height="50"
|
||||||
Margin="0 6"
|
Kind="BoxStar">
|
||||||
Width="50"
|
</avalonia:MaterialIcon>
|
||||||
Height="50"
|
|
||||||
ClipToBounds="True">
|
|
||||||
<avalonia:MaterialIcon Kind="Download"></avalonia:MaterialIcon>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<!-- Body -->
|
<!-- Body -->
|
||||||
<StackPanel Grid.Column="1" Margin="10 0" VerticalAlignment="Center">
|
<StackPanel Grid.Column="1" Margin="10 0" VerticalAlignment="Center">
|
||||||
<TextBlock Text="{CompiledBinding Entry.LatestRelease.Version, FallbackValue=Version}"></TextBlock>
|
<TextBlock Text="{CompiledBinding LatestRelease.Version, FallbackValue=Version}"></TextBlock>
|
||||||
<TextBlock Classes="subtitle">
|
<TextBlock Classes="subtitle">
|
||||||
<avalonia:MaterialIcon Kind="BoxOutline" />
|
<avalonia:MaterialIcon Kind="File" />
|
||||||
<Run Text="{CompiledBinding Entry.LatestRelease.DownloadSize, Converter={StaticResource BytesToStringConverter}, Mode=OneWay}"></Run>
|
<Run Text="{CompiledBinding LatestRelease.DownloadSize, Converter={StaticResource BytesToStringConverter}, Mode=OneWay}"></Run>
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
<TextBlock Classes="subtitle"
|
<TextBlock Classes="subtitle"
|
||||||
ToolTip.Tip="{CompiledBinding Entry.LatestRelease.CreatedAt, Converter={StaticResource DateTimeConverter}}">
|
ToolTip.Tip="{CompiledBinding LatestRelease.CreatedAt, Converter={StaticResource DateTimeConverter}}">
|
||||||
<avalonia:MaterialIcon Kind="Calendar" />
|
<avalonia:MaterialIcon Kind="Calendar" />
|
||||||
<Run>Created</Run>
|
<Run>Created</Run>
|
||||||
<Run Text="{CompiledBinding Entry.LatestRelease.CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter='humanize'}"></Run>
|
<Run Text="{CompiledBinding LatestRelease.CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter='humanize'}"></Run>
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
<ItemsControl ItemsSource="{CompiledBinding OtherReleases}">
|
||||||
|
<ItemsControl.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<StackPanel Spacing="5" />
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ItemsControl.ItemsPanel>
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="workshop:IRelease">
|
||||||
|
<Button HorizontalAlignment="Stretch"
|
||||||
|
HorizontalContentAlignment="Stretch"
|
||||||
|
Command="{Binding $parent[details:EntryReleasesView].DataContext.NavigateToRelease}"
|
||||||
|
CommandParameter="{CompiledBinding}">
|
||||||
|
<Grid ColumnDefinitions="Auto,*">
|
||||||
|
<avalonia:MaterialIcon Grid.Column="0"
|
||||||
|
Margin="0 6"
|
||||||
|
Width="25"
|
||||||
|
Height="25"
|
||||||
|
Kind="Archive">
|
||||||
|
</avalonia:MaterialIcon>
|
||||||
|
|
||||||
|
<!-- Body -->
|
||||||
|
<StackPanel Grid.Column="1" Margin="10 0" VerticalAlignment="Center">
|
||||||
|
<TextBlock Text="{CompiledBinding Version, FallbackValue=Version}"></TextBlock>
|
||||||
|
<TextBlock Classes="subtitle" ToolTip.Tip="{CompiledBinding CreatedAt, Converter={StaticResource DateTimeConverter}}">
|
||||||
|
<avalonia:MaterialIcon Kind="Calendar" />
|
||||||
|
<Run>Created</Run>
|
||||||
|
<Run Text="{CompiledBinding CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter='humanize'}"></Run>
|
||||||
|
</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Button>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@ -1,8 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Reactive;
|
using System.Reactive;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Artemis.UI.Shared;
|
using Artemis.UI.Shared;
|
||||||
|
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;
|
||||||
using Artemis.UI.Shared.Utilities;
|
using Artemis.UI.Shared.Utilities;
|
||||||
@ -19,34 +22,49 @@ public class EntryReleasesViewModel : ViewModelBase
|
|||||||
private readonly EntryInstallationHandlerFactory _factory;
|
private readonly EntryInstallationHandlerFactory _factory;
|
||||||
private readonly IWindowService _windowService;
|
private readonly IWindowService _windowService;
|
||||||
private readonly INotificationService _notificationService;
|
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;
|
_factory = factory;
|
||||||
_windowService = windowService;
|
_windowService = windowService;
|
||||||
_notificationService = notificationService;
|
_notificationService = notificationService;
|
||||||
|
_router = router;
|
||||||
|
|
||||||
Entry = entry;
|
Entry = entry;
|
||||||
|
LatestRelease = Entry.Releases.MaxBy(r => r.CreatedAt);
|
||||||
|
OtherReleases = Entry.Releases.OrderByDescending(r => r.CreatedAt).Skip(1).Take(4).Cast<IRelease>().ToList();
|
||||||
|
|
||||||
DownloadLatestRelease = ReactiveCommand.CreateFromTask(ExecuteDownloadLatestRelease);
|
DownloadLatestRelease = ReactiveCommand.CreateFromTask(ExecuteDownloadLatestRelease);
|
||||||
OnInstallationStarted = Confirm;
|
OnInstallationStarted = Confirm;
|
||||||
|
NavigateToRelease = ReactiveCommand.CreateFromTask<IRelease>(ExecuteNavigateToRelease);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEntryDetails Entry { get; }
|
public IEntryDetails Entry { get; }
|
||||||
public ReactiveCommand<Unit, Unit> DownloadLatestRelease { get; }
|
public IRelease? LatestRelease { get; }
|
||||||
|
public List<IRelease> OtherReleases { get; }
|
||||||
|
|
||||||
public Func<IEntryDetails, Task<bool>> OnInstallationStarted { get; set; }
|
public ReactiveCommand<Unit, Unit> DownloadLatestRelease { get; }
|
||||||
|
public ReactiveCommand<IRelease, Unit> NavigateToRelease { get; }
|
||||||
|
|
||||||
|
public Func<IEntryDetails, IRelease, Task<bool>> OnInstallationStarted { get; set; }
|
||||||
public Func<InstalledEntry, Task>? OnInstallationFinished { get; set; }
|
public Func<InstalledEntry, Task>? 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)
|
private async Task ExecuteDownloadLatestRelease(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (Entry.LatestRelease == null)
|
if (LatestRelease == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (await OnInstallationStarted(Entry))
|
if (await OnInstallationStarted(Entry, LatestRelease))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
IEntryInstallationHandler installationHandler = _factory.CreateHandler(Entry.EntryType);
|
IEntryInstallationHandler installationHandler = _factory.CreateHandler(Entry.EntryType);
|
||||||
EntryInstallResult result = await installationHandler.InstallAsync(Entry, Entry.LatestRelease, new Progress<StreamProgress>(), cancellationToken);
|
EntryInstallResult result = await installationHandler.InstallAsync(Entry, LatestRelease, new Progress<StreamProgress>(), cancellationToken);
|
||||||
if (result.IsSuccess && result.Entry != null)
|
if (result.IsSuccess && result.Entry != null)
|
||||||
{
|
{
|
||||||
if (OnInstallationFinished != null)
|
if (OnInstallationFinished != null)
|
||||||
@ -62,13 +80,13 @@ public class EntryReleasesViewModel : ViewModelBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> Confirm(IEntryDetails entryDetails)
|
private async Task<bool> Confirm(IEntryDetails entryDetails, IRelease release)
|
||||||
{
|
{
|
||||||
bool confirm = await _windowService.ShowConfirmContentDialog(
|
bool confirm = await _windowService.ShowConfirmContentDialog(
|
||||||
"Install latest release",
|
"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;
|
return !confirm;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -18,7 +18,7 @@ public partial class EntriesView : ReactiveUserControl<EntriesViewModel>
|
|||||||
|
|
||||||
private void Navigate(ViewModelBase viewModel)
|
private void Navigate(ViewModelBase viewModel)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Invoke(() => TabFrame.NavigateFromObject(viewModel));
|
TabFrame.NavigateFromObject(viewModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void NavigationView_OnBackRequested(object? sender, NavigationViewBackRequestedEventArgs e)
|
private void NavigationView_OnBackRequested(object? sender, NavigationViewBackRequestedEventArgs e)
|
||||||
|
|||||||
@ -53,8 +53,8 @@ public partial class EntriesViewModel : RoutableHostScreen<RoutableScreen>
|
|||||||
|
|
||||||
public void GoBack()
|
public void GoBack()
|
||||||
{
|
{
|
||||||
if (ViewingDetails)
|
if (ViewingDetails && SelectedTab != null)
|
||||||
_router.GoBack();
|
_router.Navigate(SelectedTab.Path);
|
||||||
else
|
else
|
||||||
_router.Navigate("workshop");
|
_router.Navigate("workshop");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,57 @@
|
|||||||
|
<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:list="clr-namespace:Artemis.UI.Screens.Workshop.Entries.List"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Artemis.UI.Screens.Workshop.Entries.List.EntryListView"
|
||||||
|
x:DataType="list:EntryListViewModel">
|
||||||
|
<UserControl.Styles>
|
||||||
|
<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">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}">Categories</TextBlock>
|
||||||
|
<Border Classes="card-separator" />
|
||||||
|
<ContentControl Content="{CompiledBinding CategoriesViewModel}"></ContentControl>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<ProgressBar Grid.Column="1" Grid.Row="0" VerticalAlignment="Top" Margin="0 0 20 0" IsVisible="{CompiledBinding FetchingMore}" IsIndeterminate="True" />
|
||||||
|
<ContentControl Grid.Column="1" Grid.Row="0" Margin="0 0 20 8" Content="{CompiledBinding InputViewModel}" />
|
||||||
|
|
||||||
|
<ScrollViewer Name="EntriesScrollViewer" Grid.Column="1" Grid.Row="1" ScrollChanged="ScrollViewer_OnScrollChanged" Offset="{CompiledBinding ScrollOffset}">
|
||||||
|
<ItemsControl ItemsSource="{CompiledBinding Entries}" Margin="0 0 20 0">
|
||||||
|
<ItemsControl.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<VirtualizingStackPanel />
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ItemsControl.ItemsPanel>
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<ContentControl Content="{CompiledBinding}" Margin="0 0 0 5"></ContentControl>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</ScrollViewer>
|
||||||
|
|
||||||
|
<Panel Grid.Column="1" Grid.Row="1" IsVisible="{CompiledBinding !Initializing}">
|
||||||
|
<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 entries</Run>
|
||||||
|
</TextBlock>
|
||||||
|
<Lottie Path="/Assets/Animations/empty.json" RepeatCount="1" Width="350" Height="350"></Lottie>
|
||||||
|
</StackPanel>
|
||||||
|
</Panel>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
@ -1,38 +1,30 @@
|
|||||||
using System;
|
|
||||||
using System.Reactive.Disposables;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Artemis.UI.Shared.Routing;
|
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.ReactiveUI;
|
using Avalonia.ReactiveUI;
|
||||||
using Avalonia.Threading;
|
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
|
|
||||||
namespace Artemis.UI.Screens.Workshop.Entries.Tabs;
|
namespace Artemis.UI.Screens.Workshop.Entries.List;
|
||||||
|
|
||||||
public partial class LayoutListView : ReactiveUserControl<LayoutListViewModel>
|
public partial class EntryListView : ReactiveUserControl<EntryListViewModel>
|
||||||
{
|
{
|
||||||
public LayoutListView()
|
public EntryListView()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
EntriesScrollViewer.SizeChanged += (_, _) => UpdateEntriesPerFetch();
|
EntriesScrollViewer.SizeChanged += (_, _) => UpdateEntriesPerFetch();
|
||||||
|
|
||||||
this.WhenActivated(d =>
|
this.WhenActivated(_ => UpdateEntriesPerFetch());
|
||||||
{
|
|
||||||
UpdateEntriesPerFetch();
|
|
||||||
ViewModel.WhenAnyValue(vm => vm.Screen).WhereNotNull().Subscribe(Navigate).DisposeWith(d);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ScrollViewer_OnScrollChanged(object? sender, ScrollChangedEventArgs e)
|
private void ScrollViewer_OnScrollChanged(object? sender, ScrollChangedEventArgs e)
|
||||||
{
|
{
|
||||||
|
if (ViewModel == null)
|
||||||
|
return;
|
||||||
|
|
||||||
// When near the bottom of EntriesScrollViewer, call FetchMore on the view model
|
// 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)
|
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)
|
ViewModel.ScrollOffset = EntriesScrollViewer.Offset;
|
||||||
{
|
|
||||||
Dispatcher.UIThread.Invoke(() => RouterFrame.NavigateFromObject(viewModel), DispatcherPriority.ApplicationIdle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateEntriesPerFetch()
|
private void UpdateEntriesPerFetch()
|
||||||
@ -6,6 +6,7 @@ using System.Reactive.Disposables;
|
|||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Artemis.UI.Extensions;
|
||||||
using Artemis.UI.Screens.Workshop.Categories;
|
using Artemis.UI.Screens.Workshop.Categories;
|
||||||
using Artemis.UI.Shared.Routing;
|
using Artemis.UI.Shared.Routing;
|
||||||
using Artemis.UI.Shared.Services;
|
using Artemis.UI.Shared.Services;
|
||||||
@ -15,29 +16,28 @@ using DynamicData;
|
|||||||
using PropertyChanged.SourceGenerator;
|
using PropertyChanged.SourceGenerator;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
using StrawberryShake;
|
using StrawberryShake;
|
||||||
|
using Vector = Avalonia.Vector;
|
||||||
|
|
||||||
namespace Artemis.UI.Screens.Workshop.Entries.List;
|
namespace Artemis.UI.Screens.Workshop.Entries.List;
|
||||||
|
|
||||||
public abstract partial class EntryListViewModel : RoutableHostScreen<RoutableScreen>
|
public partial class EntryListViewModel : RoutableScreen
|
||||||
{
|
{
|
||||||
private readonly SourceList<IEntrySummary> _entries = new();
|
private readonly SourceList<IEntrySummary> _entries = new();
|
||||||
private readonly INotificationService _notificationService;
|
private readonly INotificationService _notificationService;
|
||||||
private readonly IWorkshopClient _workshopClient;
|
private readonly IWorkshopClient _workshopClient;
|
||||||
private readonly string _route;
|
|
||||||
private IGetEntriesv2_EntriesV2_PageInfo? _currentPageInfo;
|
private IGetEntriesv2_EntriesV2_PageInfo? _currentPageInfo;
|
||||||
|
|
||||||
[Notify] private bool _initializing = true;
|
[Notify] private bool _initializing = true;
|
||||||
[Notify] private bool _fetchingMore;
|
[Notify] private bool _fetchingMore;
|
||||||
[Notify] private int _entriesPerFetch;
|
[Notify] private int _entriesPerFetch;
|
||||||
|
[Notify] private Vector _scrollOffset;
|
||||||
|
|
||||||
protected EntryListViewModel(string route,
|
protected EntryListViewModel(IWorkshopClient workshopClient,
|
||||||
IWorkshopClient workshopClient,
|
|
||||||
CategoriesViewModel categoriesViewModel,
|
CategoriesViewModel categoriesViewModel,
|
||||||
EntryListInputViewModel entryListInputViewModel,
|
EntryListInputViewModel entryListInputViewModel,
|
||||||
INotificationService notificationService,
|
INotificationService notificationService,
|
||||||
Func<IEntrySummary, EntryListItemViewModel> getEntryListViewModel)
|
Func<IEntrySummary, EntryListItemViewModel> getEntryListViewModel)
|
||||||
{
|
{
|
||||||
_route = route;
|
|
||||||
_workshopClient = workshopClient;
|
_workshopClient = workshopClient;
|
||||||
_notificationService = notificationService;
|
_notificationService = notificationService;
|
||||||
|
|
||||||
@ -50,37 +50,31 @@ public abstract partial class EntryListViewModel : RoutableHostScreen<RoutableSc
|
|||||||
.Subscribe();
|
.Subscribe();
|
||||||
Entries = entries;
|
Entries = entries;
|
||||||
|
|
||||||
|
// Respond to filter query input changes
|
||||||
this.WhenActivated(d =>
|
this.WhenActivated(d =>
|
||||||
{
|
{
|
||||||
// Respond to filter query input changes
|
|
||||||
InputViewModel.WhenAnyValue(vm => vm.Search).Skip(1).Throttle(TimeSpan.FromMilliseconds(200)).Subscribe(_ => Reset()).DisposeWith(d);
|
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);
|
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 CategoriesViewModel CategoriesViewModel { get; }
|
||||||
public EntryListInputViewModel InputViewModel { get; }
|
public EntryListInputViewModel InputViewModel { get; }
|
||||||
|
public EntryType? EntryType { get; set; }
|
||||||
|
|
||||||
public ReadOnlyObservableCollection<EntryListItemViewModel> Entries { get; }
|
public ReadOnlyObservableCollection<EntryListItemViewModel> 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)
|
public async Task FetchMore(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (FetchingMore || _currentPageInfo != null && !_currentPageInfo.HasNextPage)
|
if (FetchingMore || _currentPageInfo != null && !_currentPageInfo.HasNextPage)
|
||||||
@ -119,12 +113,19 @@ public abstract partial class EntryListViewModel : RoutableHostScreen<RoutableSc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual EntryFilterInput GetFilter()
|
private EntryFilterInput GetFilter()
|
||||||
{
|
{
|
||||||
return new EntryFilterInput {And = CategoriesViewModel.CategoryFilters};
|
return new EntryFilterInput
|
||||||
|
{
|
||||||
|
And =
|
||||||
|
[
|
||||||
|
new EntryFilterInput {EntryType = new EntryTypeOperationFilterInput {Eq = EntryType}},
|
||||||
|
..CategoriesViewModel.CategoryFilters ?? []
|
||||||
|
]
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual IReadOnlyList<EntrySortInput> GetSort()
|
private IReadOnlyList<EntrySortInput> GetSort()
|
||||||
{
|
{
|
||||||
// Sort by created at
|
// Sort by created at
|
||||||
if (InputViewModel.SortBy == 1)
|
if (InputViewModel.SortBy == 1)
|
||||||
|
|||||||
@ -1,67 +0,0 @@
|
|||||||
<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:tabs="clr-namespace:Artemis.UI.Screens.Workshop.Entries.Tabs"
|
|
||||||
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
|
||||||
xmlns:ui="clr-namespace:Artemis.UI"
|
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
|
||||||
x:Class="Artemis.UI.Screens.Workshop.Entries.Tabs.LayoutListView"
|
|
||||||
x:DataType="tabs:LayoutListViewModel">
|
|
||||||
<UserControl.Styles>
|
|
||||||
<Styles>
|
|
||||||
<Style Selector="StackPanel.empty-state > TextBlock">
|
|
||||||
<Setter Property="TextAlignment" Value="Center"></Setter>
|
|
||||||
<Setter Property="TextWrapping" Value="Wrap"></Setter>
|
|
||||||
</Style>
|
|
||||||
</Styles>
|
|
||||||
</UserControl.Styles>
|
|
||||||
|
|
||||||
<Panel>
|
|
||||||
<Grid ColumnDefinitions="300,*" RowDefinitions="Auto,*,Auto" IsVisible="{CompiledBinding Screen, Converter={x:Static ObjectConverters.IsNull}}">
|
|
||||||
<StackPanel Grid.Column="0" Grid.RowSpan="3" Margin="0 0 10 0" VerticalAlignment="Top">
|
|
||||||
<Border Classes="card" VerticalAlignment="Stretch">
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}">Categories</TextBlock>
|
|
||||||
<Border Classes="card-separator" />
|
|
||||||
<ContentControl Content="{CompiledBinding CategoriesViewModel}"></ContentControl>
|
|
||||||
</StackPanel>
|
|
||||||
</Border>
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<ProgressBar Grid.Column="1" Grid.Row="0" VerticalAlignment="Top" Margin="0 0 20 0" IsVisible="{CompiledBinding FetchingMore}" IsIndeterminate="True" />
|
|
||||||
<ContentControl Grid.Column="1" Grid.Row="0" Margin="0 0 20 8" Content="{CompiledBinding InputViewModel}" />
|
|
||||||
|
|
||||||
<ScrollViewer Name="EntriesScrollViewer" Grid.Column="1" Grid.Row="1" ScrollChanged="ScrollViewer_OnScrollChanged">
|
|
||||||
<ItemsControl ItemsSource="{CompiledBinding Entries}" Margin="0 0 20 0">
|
|
||||||
<ItemsControl.ItemsPanel>
|
|
||||||
<ItemsPanelTemplate>
|
|
||||||
<VirtualizingStackPanel />
|
|
||||||
</ItemsPanelTemplate>
|
|
||||||
</ItemsControl.ItemsPanel>
|
|
||||||
<ItemsControl.ItemTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<ContentControl Content="{CompiledBinding}" Margin="0 0 0 5"></ContentControl>
|
|
||||||
</DataTemplate>
|
|
||||||
</ItemsControl.ItemTemplate>
|
|
||||||
</ItemsControl>
|
|
||||||
</ScrollViewer>
|
|
||||||
|
|
||||||
<Panel Grid.Column="1" Grid.Row="1" IsVisible="{CompiledBinding !Initializing}">
|
|
||||||
<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>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<controls:Frame Name="RouterFrame" IsNavigationStackEnabled="False" CacheSize="0" IsVisible="{CompiledBinding Screen, Converter={x:Static ObjectConverters.IsNotNull}}">
|
|
||||||
<controls:Frame.NavigationPageFactory>
|
|
||||||
<ui:PageFactory />
|
|
||||||
</controls:Frame.NavigationPageFactory>
|
|
||||||
</controls:Frame>
|
|
||||||
</Panel>
|
|
||||||
</UserControl>
|
|
||||||
@ -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<IEntrySummary, EntryListItemViewModel> 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()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,67 +0,0 @@
|
|||||||
<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:tabs="clr-namespace:Artemis.UI.Screens.Workshop.Entries.Tabs"
|
|
||||||
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
|
||||||
xmlns:ui="clr-namespace:Artemis.UI"
|
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
|
||||||
x:Class="Artemis.UI.Screens.Workshop.Entries.Tabs.PluginListView"
|
|
||||||
x:DataType="tabs:PluginListViewModel">
|
|
||||||
<UserControl.Styles>
|
|
||||||
<Styles>
|
|
||||||
<Style Selector="StackPanel.empty-state > TextBlock">
|
|
||||||
<Setter Property="TextAlignment" Value="Center"></Setter>
|
|
||||||
<Setter Property="TextWrapping" Value="Wrap"></Setter>
|
|
||||||
</Style>
|
|
||||||
</Styles>
|
|
||||||
</UserControl.Styles>
|
|
||||||
|
|
||||||
<Panel>
|
|
||||||
<Grid ColumnDefinitions="300,*" RowDefinitions="Auto,*,Auto" IsVisible="{CompiledBinding Screen, Converter={x:Static ObjectConverters.IsNull}}">
|
|
||||||
<StackPanel Grid.Column="0" Grid.RowSpan="3" Margin="0 0 10 0" VerticalAlignment="Top">
|
|
||||||
<Border Classes="card" VerticalAlignment="Stretch">
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}">Categories</TextBlock>
|
|
||||||
<Border Classes="card-separator" />
|
|
||||||
<ContentControl Content="{CompiledBinding CategoriesViewModel}"></ContentControl>
|
|
||||||
</StackPanel>
|
|
||||||
</Border>
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<ProgressBar Grid.Column="1" Grid.Row="0" VerticalAlignment="Top" Margin="0 0 20 0" IsVisible="{CompiledBinding FetchingMore}" IsIndeterminate="True" />
|
|
||||||
<ContentControl Grid.Column="1" Grid.Row="0" Margin="0 0 20 8" Content="{CompiledBinding InputViewModel}" />
|
|
||||||
|
|
||||||
<ScrollViewer Name="EntriesScrollViewer" Grid.Column="1" Grid.Row="1" ScrollChanged="ScrollViewer_OnScrollChanged">
|
|
||||||
<ItemsControl ItemsSource="{CompiledBinding Entries}" Margin="0 0 20 0">
|
|
||||||
<ItemsControl.ItemsPanel>
|
|
||||||
<ItemsPanelTemplate>
|
|
||||||
<VirtualizingStackPanel />
|
|
||||||
</ItemsPanelTemplate>
|
|
||||||
</ItemsControl.ItemsPanel>
|
|
||||||
<ItemsControl.ItemTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<ContentControl Content="{CompiledBinding}" Margin="0 0 0 5"></ContentControl>
|
|
||||||
</DataTemplate>
|
|
||||||
</ItemsControl.ItemTemplate>
|
|
||||||
</ItemsControl>
|
|
||||||
</ScrollViewer>
|
|
||||||
|
|
||||||
<Panel Grid.Column="1" Grid.Row="1" IsVisible="{CompiledBinding !Initializing}">
|
|
||||||
<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 plugins</Run>
|
|
||||||
</TextBlock>
|
|
||||||
<Lottie Path="/Assets/Animations/empty.json" RepeatCount="1" Width="350" Height="350"></Lottie>
|
|
||||||
</StackPanel>
|
|
||||||
</Panel>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<controls:Frame Name="RouterFrame" IsNavigationStackEnabled="False" CacheSize="0" IsVisible="{CompiledBinding Screen, Converter={x:Static ObjectConverters.IsNotNull}}">
|
|
||||||
<controls:Frame.NavigationPageFactory>
|
|
||||||
<ui:PageFactory />
|
|
||||||
</controls:Frame.NavigationPageFactory>
|
|
||||||
</controls:Frame>
|
|
||||||
</Panel>
|
|
||||||
</UserControl>
|
|
||||||
@ -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<PluginListViewModel>
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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<IEntrySummary, EntryListItemViewModel> 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()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,67 +0,0 @@
|
|||||||
<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:tabs="clr-namespace:Artemis.UI.Screens.Workshop.Entries.Tabs"
|
|
||||||
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
|
||||||
xmlns:ui="clr-namespace:Artemis.UI"
|
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
|
||||||
x:Class="Artemis.UI.Screens.Workshop.Entries.Tabs.ProfileListView"
|
|
||||||
x:DataType="tabs:ProfileListViewModel">
|
|
||||||
<UserControl.Styles>
|
|
||||||
<Styles>
|
|
||||||
<Style Selector="StackPanel.empty-state > TextBlock">
|
|
||||||
<Setter Property="TextAlignment" Value="Center"></Setter>
|
|
||||||
<Setter Property="TextWrapping" Value="Wrap"></Setter>
|
|
||||||
</Style>
|
|
||||||
</Styles>
|
|
||||||
</UserControl.Styles>
|
|
||||||
|
|
||||||
<Panel>
|
|
||||||
<Grid ColumnDefinitions="300,*" RowDefinitions="Auto,*,Auto" IsVisible="{CompiledBinding Screen, Converter={x:Static ObjectConverters.IsNull}}">
|
|
||||||
<StackPanel Grid.Column="0" Grid.RowSpan="3" Margin="0 0 10 0" VerticalAlignment="Top">
|
|
||||||
<Border Classes="card" VerticalAlignment="Stretch">
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}">Categories</TextBlock>
|
|
||||||
<Border Classes="card-separator" />
|
|
||||||
<ContentControl Content="{CompiledBinding CategoriesViewModel}"></ContentControl>
|
|
||||||
</StackPanel>
|
|
||||||
</Border>
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<ProgressBar Grid.Column="1" Grid.Row="0" VerticalAlignment="Top" Margin="0 0 20 0" IsVisible="{CompiledBinding FetchingMore}" IsIndeterminate="True" />
|
|
||||||
<ContentControl Grid.Column="1" Grid.Row="0" Margin="0 0 20 8" Content="{CompiledBinding InputViewModel}" />
|
|
||||||
|
|
||||||
<ScrollViewer Name="EntriesScrollViewer" Grid.Column="1" Grid.Row="1" ScrollChanged="ScrollViewer_OnScrollChanged">
|
|
||||||
<ItemsControl ItemsSource="{CompiledBinding Entries}" Margin="0 0 20 0">
|
|
||||||
<ItemsControl.ItemsPanel>
|
|
||||||
<ItemsPanelTemplate>
|
|
||||||
<VirtualizingStackPanel />
|
|
||||||
</ItemsPanelTemplate>
|
|
||||||
</ItemsControl.ItemsPanel>
|
|
||||||
<ItemsControl.ItemTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<ContentControl Content="{CompiledBinding}" Margin="0 0 0 5"></ContentControl>
|
|
||||||
</DataTemplate>
|
|
||||||
</ItemsControl.ItemTemplate>
|
|
||||||
</ItemsControl>
|
|
||||||
</ScrollViewer>
|
|
||||||
|
|
||||||
<Panel Grid.Column="1" Grid.Row="1" IsVisible="{CompiledBinding !Initializing}">
|
|
||||||
<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>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<controls:Frame Name="RouterFrame" IsNavigationStackEnabled="False" CacheSize="0" IsVisible="{CompiledBinding Screen, Converter={x:Static ObjectConverters.IsNotNull}}">
|
|
||||||
<controls:Frame.NavigationPageFactory>
|
|
||||||
<ui:PageFactory />
|
|
||||||
</controls:Frame.NavigationPageFactory>
|
|
||||||
</controls:Frame>
|
|
||||||
</Panel>
|
|
||||||
</UserControl>
|
|
||||||
@ -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<ProfileListViewModel>
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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<IEntrySummary, EntryListItemViewModel> 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()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<Border Classes="card" VerticalAlignment="Top">
|
<Border Classes="card" VerticalAlignment="Top">
|
||||||
<ContentControl Content="{CompiledBinding EntryInfoViewModel}" />
|
<ContentControl Content="{CompiledBinding EntryInfoViewModel}" />
|
||||||
</Border>
|
</Border>
|
||||||
<Border Classes="card" VerticalAlignment="Top" IsVisible="{CompiledBinding Entry.LatestRelease, Converter={x:Static ObjectConverters.IsNotNull}}">
|
<Border Classes="card" VerticalAlignment="Top" IsVisible="{CompiledBinding Entry.Releases.Count}">
|
||||||
<ContentControl Content="{CompiledBinding EntryReleasesViewModel}" />
|
<ContentControl Content="{CompiledBinding EntryReleasesViewModel}" />
|
||||||
</Border>
|
</Border>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|||||||
16
src/Artemis.UI/Screens/Workshop/Layout/LayoutListView.axaml
Normal file
16
src/Artemis.UI/Screens/Workshop/Layout/LayoutListView.axaml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<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:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
|
xmlns:ui="clr-namespace:Artemis.UI"
|
||||||
|
xmlns:layout="clr-namespace:Artemis.UI.Screens.Workshop.Layout"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Artemis.UI.Screens.Workshop.Layout.LayoutListView"
|
||||||
|
x:DataType="layout:LayoutListViewModel">
|
||||||
|
<controls:Frame Name="RouterFrame" IsNavigationStackEnabled="False" CacheSize="0">
|
||||||
|
<controls:Frame.NavigationPageFactory>
|
||||||
|
<ui:PageFactory />
|
||||||
|
</controls:Frame.NavigationPageFactory>
|
||||||
|
</controls:Frame>
|
||||||
|
</UserControl>
|
||||||
@ -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<LayoutListViewModel>
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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<RoutableScreen>
|
||||||
|
{
|
||||||
|
public EntryListViewModel EntryListViewModel { get; }
|
||||||
|
|
||||||
|
public LayoutListViewModel(EntryListViewModel entryListViewModel)
|
||||||
|
{
|
||||||
|
EntryListViewModel = entryListViewModel;
|
||||||
|
EntryListViewModel.EntryType = EntryType.Layout;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -18,9 +18,9 @@ public partial class WorkshopLibraryView : ReactiveUserControl<WorkshopLibraryVi
|
|||||||
|
|
||||||
private void Navigate(ViewModelBase viewModel)
|
private void Navigate(ViewModelBase viewModel)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Invoke(() => TabFrame.NavigateFromObject(viewModel));
|
TabFrame.NavigateFromObject(viewModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void NavigationView_OnBackRequested(object? sender, NavigationViewBackRequestedEventArgs e)
|
private void NavigationView_OnBackRequested(object? sender, NavigationViewBackRequestedEventArgs e)
|
||||||
{
|
{
|
||||||
ViewModel?.GoBack();
|
ViewModel?.GoBack();
|
||||||
|
|||||||
@ -29,7 +29,7 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<Border Classes="card" VerticalAlignment="Top" IsVisible="{CompiledBinding Entry.LatestRelease, Converter={x:Static ObjectConverters.IsNotNull}}">
|
<Border Classes="card" VerticalAlignment="Top" IsVisible="{CompiledBinding Entry.Releases.Count}">
|
||||||
<ContentControl Content="{CompiledBinding EntryReleasesViewModel}" />
|
<ContentControl Content="{CompiledBinding EntryReleasesViewModel}" />
|
||||||
</Border>
|
</Border>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
@ -49,7 +49,13 @@
|
|||||||
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}">Used by these profiles</TextBlock>
|
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}">Used by these profiles</TextBlock>
|
||||||
<Border Classes="card-separator" />
|
<Border Classes="card-separator" />
|
||||||
<ScrollViewer>
|
<ScrollViewer>
|
||||||
<ItemsControl ItemsSource="{CompiledBinding Dependants}"></ItemsControl>
|
<ItemsControl ItemsSource="{CompiledBinding Dependants}">
|
||||||
|
<ItemsControl.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<StackPanel Spacing="5"></StackPanel>
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ItemsControl.ItemsPanel>
|
||||||
|
</ItemsControl>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|||||||
@ -33,7 +33,7 @@ public partial class PluginDetailsViewModel : RoutableScreen<WorkshopDetailParam
|
|||||||
[Notify] private EntryReleasesViewModel? _entryReleasesViewModel;
|
[Notify] private EntryReleasesViewModel? _entryReleasesViewModel;
|
||||||
[Notify] private EntryImagesViewModel? _entryImagesViewModel;
|
[Notify] private EntryImagesViewModel? _entryImagesViewModel;
|
||||||
[Notify] private ReadOnlyObservableCollection<EntryListItemViewModel>? _dependants;
|
[Notify] private ReadOnlyObservableCollection<EntryListItemViewModel>? _dependants;
|
||||||
|
|
||||||
public PluginDetailsViewModel(IWorkshopClient client,
|
public PluginDetailsViewModel(IWorkshopClient client,
|
||||||
IWindowService windowService,
|
IWindowService windowService,
|
||||||
IPluginManagementService pluginManagementService,
|
IPluginManagementService pluginManagementService,
|
||||||
@ -72,18 +72,21 @@ public partial class PluginDetailsViewModel : RoutableScreen<WorkshopDetailParam
|
|||||||
EntryReleasesViewModel.OnInstallationStarted = OnInstallationStarted;
|
EntryReleasesViewModel.OnInstallationStarted = OnInstallationStarted;
|
||||||
EntryReleasesViewModel.OnInstallationFinished = OnInstallationFinished;
|
EntryReleasesViewModel.OnInstallationFinished = OnInstallationFinished;
|
||||||
}
|
}
|
||||||
|
|
||||||
IReadOnlyList<IEntrySummary>? dependants = (await _client.GetDependantEntries.ExecuteAsync(entryId, 0, 25, cancellationToken)).Data?.Entries?.Items;
|
IReadOnlyList<IEntrySummary>? dependants = (await _client.GetDependantEntries.ExecuteAsync(entryId, 0, 25, cancellationToken)).Data?.Entries?.Items;
|
||||||
Dependants = dependants != null && dependants.Any()
|
Dependants = dependants != null && dependants.Any()
|
||||||
? new ReadOnlyObservableCollection<EntryListItemViewModel>(new ObservableCollection<EntryListItemViewModel>(dependants.Select(_getEntryListViewModel)))
|
? new ReadOnlyObservableCollection<EntryListItemViewModel>(new ObservableCollection<EntryListItemViewModel>(dependants
|
||||||
|
.Select(_getEntryListViewModel)
|
||||||
|
.OrderByDescending(d => d.Entry.Downloads)
|
||||||
|
.Take(10)))
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> OnInstallationStarted(IEntryDetails entryDetails)
|
private async Task<bool> OnInstallationStarted(IEntryDetails entryDetails, IRelease release)
|
||||||
{
|
{
|
||||||
bool confirm = await _windowService.ShowConfirmContentDialog(
|
bool confirm = await _windowService.ShowConfirmContentDialog(
|
||||||
"Installing plugin",
|
"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!",
|
"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"
|
"I trust this plugin, install it"
|
||||||
);
|
);
|
||||||
|
|||||||
16
src/Artemis.UI/Screens/Workshop/Plugins/PluginListView.axaml
Normal file
16
src/Artemis.UI/Screens/Workshop/Plugins/PluginListView.axaml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<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:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
|
xmlns:ui="clr-namespace:Artemis.UI"
|
||||||
|
xmlns:plugins="clr-namespace:Artemis.UI.Screens.Workshop.Plugins"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Artemis.UI.Screens.Workshop.Plugins.PluginListView"
|
||||||
|
x:DataType="plugins:PluginListViewModel">
|
||||||
|
<controls:Frame Name="RouterFrame" IsNavigationStackEnabled="False" CacheSize="0">
|
||||||
|
<controls:Frame.NavigationPageFactory>
|
||||||
|
<ui:PageFactory />
|
||||||
|
</controls:Frame.NavigationPageFactory>
|
||||||
|
</controls:Frame>
|
||||||
|
</UserControl>
|
||||||
@ -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<PluginListViewModel>
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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<RoutableScreen>
|
||||||
|
{
|
||||||
|
public EntryListViewModel EntryListViewModel { get; }
|
||||||
|
|
||||||
|
public PluginListViewModel(EntryListViewModel entryListViewModel)
|
||||||
|
{
|
||||||
|
EntryListViewModel = entryListViewModel;
|
||||||
|
EntryListViewModel.EntryType = EntryType.Plugin;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<Border Classes="card" VerticalAlignment="Top">
|
<Border Classes="card" VerticalAlignment="Top">
|
||||||
<ContentControl Content="{CompiledBinding EntryInfoViewModel}" />
|
<ContentControl Content="{CompiledBinding EntryInfoViewModel}" />
|
||||||
</Border>
|
</Border>
|
||||||
<Border Classes="card" VerticalAlignment="Top" IsVisible="{CompiledBinding Entry.LatestRelease, Converter={x:Static ObjectConverters.IsNotNull}}">
|
<Border Classes="card" VerticalAlignment="Top" IsVisible="{CompiledBinding Entry.Releases.Count}">
|
||||||
<ContentControl Content="{CompiledBinding EntryReleasesViewModel}" />
|
<ContentControl Content="{CompiledBinding EntryReleasesViewModel}" />
|
||||||
</Border>
|
</Border>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
@ -32,7 +32,13 @@
|
|||||||
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}">Required plugins</TextBlock>
|
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}">Required plugins</TextBlock>
|
||||||
<Border Classes="card-separator" />
|
<Border Classes="card-separator" />
|
||||||
<ScrollViewer>
|
<ScrollViewer>
|
||||||
<ItemsControl ItemsSource="{CompiledBinding Dependencies}"></ItemsControl>
|
<ItemsControl ItemsSource="{CompiledBinding Dependencies}">
|
||||||
|
<ItemsControl.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<StackPanel Spacing="5"></StackPanel>
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ItemsControl.ItemsPanel>
|
||||||
|
</ItemsControl>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|||||||
@ -0,0 +1,16 @@
|
|||||||
|
<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:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
|
xmlns:ui="clr-namespace:Artemis.UI"
|
||||||
|
xmlns:profile="clr-namespace:Artemis.UI.Screens.Workshop.Profile"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Artemis.UI.Screens.Workshop.Profile.ProfileListView"
|
||||||
|
x:DataType="profile:ProfileListViewModel">
|
||||||
|
<controls:Frame Name="RouterFrame" IsNavigationStackEnabled="False" CacheSize="0">
|
||||||
|
<controls:Frame.NavigationPageFactory>
|
||||||
|
<ui:PageFactory />
|
||||||
|
</controls:Frame.NavigationPageFactory>
|
||||||
|
</controls:Frame>
|
||||||
|
</UserControl>
|
||||||
@ -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<ProfileListViewModel>
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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<RoutableScreen>
|
||||||
|
{
|
||||||
|
public EntryListViewModel EntryListViewModel { get; }
|
||||||
|
|
||||||
|
public ProfileListViewModel(EntryListViewModel entryListViewModel)
|
||||||
|
{
|
||||||
|
EntryListViewModel = entryListViewModel;
|
||||||
|
EntryListViewModel.EntryType = EntryType.Profile;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -8,7 +8,7 @@ using ReactiveUI;
|
|||||||
|
|
||||||
namespace Artemis.UI.Screens.Workshop.SubmissionWizard;
|
namespace Artemis.UI.Screens.Workshop.SubmissionWizard;
|
||||||
|
|
||||||
public partial class ReleaseWizardView: ReactiveAppWindow<ReleaseWizardViewModel>
|
public partial class ReleaseWizardView : ReactiveAppWindow<ReleaseWizardViewModel>
|
||||||
{
|
{
|
||||||
public ReleaseWizardView()
|
public ReleaseWizardView()
|
||||||
{
|
{
|
||||||
@ -25,7 +25,7 @@ public partial class ReleaseWizardView: ReactiveAppWindow<ReleaseWizardViewModel
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Invoke(() => Frame.NavigateFromObject(viewModel));
|
Frame.NavigateFromObject(viewModel);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -25,7 +25,7 @@ public partial class SubmissionWizardView : ReactiveAppWindow<SubmissionWizardVi
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Invoke(() => Frame.NavigateFromObject(viewModel));
|
Frame.NavigateFromObject(viewModel);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -2,6 +2,11 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:avalonia="clr-namespace:Markdown.Avalonia;assembly=Markdown.Avalonia"
|
xmlns:avalonia="clr-namespace:Markdown.Avalonia;assembly=Markdown.Avalonia"
|
||||||
xmlns:ctxt="clr-namespace:ColorTextBlock.Avalonia;assembly=ColorTextBlock.Avalonia">
|
xmlns:ctxt="clr-namespace:ColorTextBlock.Avalonia;assembly=ColorTextBlock.Avalonia">
|
||||||
|
<Design.PreviewWith>
|
||||||
|
<avalonia:MarkdownScrollViewer>
|
||||||
|
Test
|
||||||
|
</avalonia:MarkdownScrollViewer>
|
||||||
|
</Design.PreviewWith>
|
||||||
<Style Selector="ScrollViewer > StackPanel">
|
<Style Selector="ScrollViewer > StackPanel">
|
||||||
<Setter Property="Margin" Value="0 0 15 0"></Setter>
|
<Setter Property="Margin" Value="0 0 15 0"></Setter>
|
||||||
</Style>
|
</Style>
|
||||||
|
|||||||
@ -54,12 +54,12 @@ fragment entryDetails on Entry {
|
|||||||
categories {
|
categories {
|
||||||
...category
|
...category
|
||||||
}
|
}
|
||||||
latestRelease {
|
|
||||||
...release
|
|
||||||
}
|
|
||||||
images {
|
images {
|
||||||
...image
|
...image
|
||||||
}
|
}
|
||||||
|
releases {
|
||||||
|
...release
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fragment release on Release {
|
fragment release on Release {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user