mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-12 13:28:33 +00:00
Workshop - Improve child navigation performance
This commit is contained in:
parent
cac44d748d
commit
62057d657a
@ -9,5 +9,6 @@ internal interface IRoutableHostScreen : IRoutableScreen
|
||||
{
|
||||
bool RecycleScreen { get; }
|
||||
IRoutableScreen? InternalScreen { get; }
|
||||
IRoutableScreen? InternalDefaultScreen { get; }
|
||||
void InternalChangeScreen(IRoutableScreen? screen);
|
||||
}
|
||||
@ -25,7 +25,13 @@ public abstract class RoutableHostScreen<TScreen> : RoutableScreen, IRoutableHos
|
||||
protected set => RaiseAndSetIfChanged(ref _recycleScreen, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the screen to show when no other screen is active.
|
||||
/// </summary>
|
||||
public virtual TScreen? DefaultScreen { get; }
|
||||
|
||||
IRoutableScreen? IRoutableHostScreen.InternalScreen => Screen;
|
||||
IRoutableScreen? IRoutableHostScreen.InternalDefaultScreen => DefaultScreen;
|
||||
|
||||
void IRoutableHostScreen.InternalChangeScreen(IRoutableScreen? screen)
|
||||
{
|
||||
|
||||
@ -27,7 +27,13 @@ public abstract class RoutableHostScreen<TScreen, TParam> : RoutableScreen<TPara
|
||||
protected set => RaiseAndSetIfChanged(ref _recycleScreen, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the screen to show when no other screen is active.
|
||||
/// </summary>
|
||||
public virtual TScreen? DefaultScreen { get; }
|
||||
|
||||
IRoutableScreen? IRoutableHostScreen.InternalScreen => Screen;
|
||||
IRoutableScreen? IRoutableHostScreen.InternalDefaultScreen => DefaultScreen;
|
||||
|
||||
void IRoutableHostScreen.InternalChangeScreen(IRoutableScreen? screen)
|
||||
{
|
||||
|
||||
@ -109,12 +109,11 @@ internal class Navigation
|
||||
// Navigate the child too
|
||||
if (resolution.Child != null)
|
||||
await NavigateResolution(resolution.Child, args, childScreen);
|
||||
// Make sure there is no child
|
||||
else if (childScreen.InternalScreen != null)
|
||||
childScreen.InternalChangeScreen(null);
|
||||
// Without a resolution, navigate to the default screen (which may be null)
|
||||
else if (childScreen.InternalScreen != childScreen.InternalDefaultScreen)
|
||||
childScreen.InternalChangeScreen(childScreen.InternalDefaultScreen);
|
||||
}
|
||||
|
||||
|
||||
Completed = true;
|
||||
}
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Margin="20" Grid.Column="0">
|
||||
<StackPanel>
|
||||
<TextBlock Theme="{StaticResource TitleTextBlockStyle}">TitleTextBlockStyle</TextBlock>
|
||||
<TextBlock Classes="h1">This is heading 1</TextBlock>
|
||||
<TextBlock Classes="h2">This is heading 2</TextBlock>
|
||||
<TextBlock Classes="h3">This is heading 3</TextBlock>
|
||||
@ -22,6 +23,7 @@
|
||||
|
||||
<Grid Margin="20" Grid.Column="1">
|
||||
<StackPanel>
|
||||
<Border Width="400" Classes="skeleton-text title"></Border>
|
||||
<Border Width="400" Classes="skeleton-text h1"></Border>
|
||||
<Border Width="400" Classes="skeleton-text h2"></Border>
|
||||
<Border Width="400" Classes="skeleton-text h3"></Border>
|
||||
@ -39,6 +41,7 @@
|
||||
<Setter Property="Background" Value="#55ff0000"></Setter>
|
||||
</Style>
|
||||
</StackPanel.Styles>
|
||||
<TextBlock Theme="{StaticResource TitleTextBlockStyle}">TitleTextBlockStyle</TextBlock>
|
||||
<TextBlock Classes="h1">This is heading 1</TextBlock>
|
||||
<TextBlock Classes="h2">This is heading 2</TextBlock>
|
||||
<TextBlock Classes="h3">This is heading 3</TextBlock>
|
||||
@ -51,6 +54,7 @@
|
||||
|
||||
<Grid Margin="20" Grid.Column="0" Row="1">
|
||||
<StackPanel Spacing="2">
|
||||
<Border Width="400" Classes="skeleton-text title no-margin"></Border>
|
||||
<Border Width="400" Classes="skeleton-text h1 no-margin"></Border>
|
||||
<Border Width="400" Classes="skeleton-text h2 no-margin"></Border>
|
||||
<Border Width="400" Classes="skeleton-text h3 no-margin"></Border>
|
||||
@ -68,6 +72,7 @@
|
||||
<Setter Property="Background" Value="#55ff0000"></Setter>
|
||||
</Style>
|
||||
</StackPanel.Styles>
|
||||
<TextBlock Theme="{StaticResource TitleTextBlockStyle}">TitleTextBlockStyle</TextBlock>
|
||||
<TextBlock Classes="h1 no-margin">This is heading 1</TextBlock>
|
||||
<TextBlock Classes="h2 no-margin">This is heading 2</TextBlock>
|
||||
<TextBlock Classes="h3 no-margin">This is heading 3</TextBlock>
|
||||
@ -125,6 +130,11 @@
|
||||
</Style.Animations>
|
||||
</Style>
|
||||
|
||||
<Style Selector="Border.skeleton-text.title">
|
||||
<Setter Property="Height" Value="28" />
|
||||
<Setter Property="Margin" Value="0 5 0 5" />
|
||||
<Setter Property="CornerRadius" Value="8" />
|
||||
</Style>
|
||||
<Style Selector="Border.skeleton-text.h1">
|
||||
<Setter Property="Height" Value="65" />
|
||||
<Setter Property="Margin" Value="0 10 0 20" />
|
||||
|
||||
@ -13,74 +13,90 @@
|
||||
<converters:EntryIconUriConverter x:Key="EntryIconUriConverter" />
|
||||
<converters:DateTimeConverter x:Key="DateTimeConverter" />
|
||||
</UserControl.Resources>
|
||||
<StackPanel>
|
||||
<Panel>
|
||||
<Border CornerRadius="6"
|
||||
HorizontalAlignment="Left"
|
||||
Margin="0 0 10 0"
|
||||
Width="80"
|
||||
Height="80"
|
||||
ClipToBounds="True">
|
||||
<Image Stretch="UniformToFill" il:ImageLoader.Source="{CompiledBinding Entry.Id, Converter={StaticResource EntryIconUriConverter}, Mode=OneWay}" />
|
||||
</Border>
|
||||
<Button Classes="icon-button"
|
||||
VerticalAlignment="Top"
|
||||
HorizontalAlignment="Right"
|
||||
Command="{CompiledBinding CopyShareLink}"
|
||||
ToolTip.Tip="Copy share link">
|
||||
<avalonia:MaterialIcon Kind="ShareVariant" />
|
||||
<Panel>
|
||||
<StackPanel IsVisible="{CompiledBinding Entry, Converter={x:Static ObjectConverters.IsNull}}">
|
||||
<Border Classes="skeleton-text" Margin="0 0 10 0" Width="80" Height="80"></Border>
|
||||
<Border Classes="skeleton-text title" HorizontalAlignment="Stretch"/>
|
||||
<Border Classes="skeleton-text" Width="120"/>
|
||||
<Border Classes="skeleton-text" Width="140" Margin="0 8"/>
|
||||
<Border Classes="skeleton-text" Width="80"/>
|
||||
<Border Classes="card-separator" Margin="0 15 0 17"></Border>
|
||||
<Border Classes="skeleton-text" Width="120"/>
|
||||
<StackPanel Margin="0 10 0 0">
|
||||
<Border Classes="skeleton-text" Width="160"/>
|
||||
<Border Classes="skeleton-text" Width="160"/>
|
||||
</StackPanel>
|
||||
<Border Classes="skeleton-button"></Border>
|
||||
</StackPanel>
|
||||
<StackPanel IsVisible="{CompiledBinding Entry, Converter={x:Static ObjectConverters.IsNotNull}}">
|
||||
<Panel>
|
||||
<Border CornerRadius="6"
|
||||
HorizontalAlignment="Left"
|
||||
Margin="0 0 10 0"
|
||||
Width="80"
|
||||
Height="80"
|
||||
ClipToBounds="True">
|
||||
<Image Stretch="UniformToFill" il:ImageLoader.Source="{CompiledBinding Entry.Id, Converter={StaticResource EntryIconUriConverter}, Mode=OneWay}" />
|
||||
</Border>
|
||||
<Button Classes="icon-button"
|
||||
VerticalAlignment="Top"
|
||||
HorizontalAlignment="Right"
|
||||
Command="{CompiledBinding CopyShareLink}"
|
||||
ToolTip.Tip="Copy share link">
|
||||
<avalonia:MaterialIcon Kind="ShareVariant" />
|
||||
</Button>
|
||||
</Panel>
|
||||
|
||||
<TextBlock Theme="{StaticResource TitleTextBlockStyle}"
|
||||
MaxLines="3"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
Text="{CompiledBinding Entry.Name, FallbackValue=Title}"/>
|
||||
|
||||
<TextBlock Classes="subtitle" TextTrimming="CharacterEllipsis" Text="{CompiledBinding Entry.Author, FallbackValue=Author}" />
|
||||
|
||||
<TextBlock Margin="0 8" TextWrapping="Wrap" Text="{CompiledBinding Entry.Summary, FallbackValue=Summary}" />
|
||||
|
||||
<!-- Categories -->
|
||||
<ItemsControl ItemsSource="{CompiledBinding Entry.Categories}" Margin="0 0 -8 0">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<WrapPanel Orientation="Horizontal"></WrapPanel>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal" Margin="0 0 8 0">
|
||||
<avalonia:MaterialIcon Kind="{CompiledBinding Icon}" Margin="0 0 3 0"></avalonia:MaterialIcon>
|
||||
<TextBlock Text="{CompiledBinding Name}" TextTrimming="CharacterEllipsis" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<Border Classes="card-separator"></Border>
|
||||
|
||||
<TextBlock Margin="0 0 0 8">
|
||||
<avalonia:MaterialIcon Kind="Downloads" />
|
||||
<Run Classes="h5" Text="{CompiledBinding Entry.Downloads, FallbackValue=0}" />
|
||||
<Run>downloads</Run>
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock Classes="subtitle"
|
||||
ToolTip.Tip="{CompiledBinding Entry.CreatedAt, Converter={StaticResource DateTimeConverter}}">
|
||||
<avalonia:MaterialIcon Kind="Calendar" />
|
||||
<Run>Created</Run>
|
||||
<Run Text="{CompiledBinding Entry.CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter='humanize'}"></Run>
|
||||
</TextBlock>
|
||||
<TextBlock Classes="subtitle"
|
||||
ToolTip.Tip="{CompiledBinding UpdatedAt, Converter={StaticResource DateTimeConverter}}">
|
||||
<avalonia:MaterialIcon Kind="Update" />
|
||||
<Run>Updated</Run>
|
||||
<Run Text="{CompiledBinding UpdatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter='humanize'}"></Run>
|
||||
</TextBlock>
|
||||
|
||||
<Button IsVisible="{CompiledBinding CanBeManaged}" Command="{CompiledBinding GoToManage}" Margin="0 10 0 0" HorizontalAlignment="Stretch">
|
||||
Manage installation
|
||||
</Button>
|
||||
</Panel>
|
||||
|
||||
<TextBlock Theme="{StaticResource TitleTextBlockStyle}"
|
||||
MaxLines="3"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
Text="{CompiledBinding Entry.Name, FallbackValue=Title }" />
|
||||
|
||||
<TextBlock Classes="subtitle" TextTrimming="CharacterEllipsis" Text="{CompiledBinding Entry.Author, FallbackValue=Author}" />
|
||||
|
||||
<TextBlock Margin="0 8" TextWrapping="Wrap" Text="{CompiledBinding Entry.Summary, FallbackValue=Summary}" />
|
||||
|
||||
<!-- Categories -->
|
||||
<ItemsControl ItemsSource="{CompiledBinding Entry.Categories}" Margin="0 0 -8 0">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<WrapPanel Orientation="Horizontal"></WrapPanel>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal" Margin="0 0 8 0">
|
||||
<avalonia:MaterialIcon Kind="{CompiledBinding Icon}" Margin="0 0 3 0"></avalonia:MaterialIcon>
|
||||
<TextBlock Text="{CompiledBinding Name}" TextTrimming="CharacterEllipsis" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<Border Classes="card-separator"></Border>
|
||||
|
||||
<TextBlock Margin="0 0 0 8">
|
||||
<avalonia:MaterialIcon Kind="Downloads" />
|
||||
<Run Classes="h5" Text="{CompiledBinding Entry.Downloads, FallbackValue=0}" />
|
||||
<Run>downloads</Run>
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock Classes="subtitle"
|
||||
ToolTip.Tip="{CompiledBinding Entry.CreatedAt, Converter={StaticResource DateTimeConverter}}">
|
||||
<avalonia:MaterialIcon Kind="Calendar" />
|
||||
<Run>Created</Run>
|
||||
<Run Text="{CompiledBinding Entry.CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter='humanize'}"></Run>
|
||||
</TextBlock>
|
||||
<TextBlock Classes="subtitle"
|
||||
ToolTip.Tip="{CompiledBinding UpdatedAt, Converter={StaticResource DateTimeConverter}}">
|
||||
<avalonia:MaterialIcon Kind="Update" />
|
||||
<Run>Updated</Run>
|
||||
<Run Text="{CompiledBinding UpdatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter='humanize'}"></Run>
|
||||
</TextBlock>
|
||||
|
||||
<Button IsVisible="{CompiledBinding CanBeManaged}" Command="{CompiledBinding GoToManage}" Margin="0 10 0 0" HorizontalAlignment="Stretch">
|
||||
Manage installation
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Panel>
|
||||
</UserControl>
|
||||
@ -8,6 +8,7 @@ using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Routing;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Artemis.WebClient.Workshop;
|
||||
using Artemis.WebClient.Workshop.Extensions;
|
||||
using Artemis.WebClient.Workshop.Models;
|
||||
using Artemis.WebClient.Workshop.Services;
|
||||
using PropertyChanged.SourceGenerator;
|
||||
@ -19,36 +20,47 @@ public partial class EntryInfoViewModel : ActivatableViewModelBase
|
||||
{
|
||||
private readonly IRouter _router;
|
||||
private readonly INotificationService _notificationService;
|
||||
private readonly IWorkshopService _workshopService;
|
||||
[Notify] private IEntryDetails? _entry;
|
||||
[Notify] private DateTimeOffset? _updatedAt;
|
||||
[Notify] private bool _canBeManaged;
|
||||
|
||||
public EntryInfoViewModel(IEntryDetails entry, IRouter router, INotificationService notificationService, IWorkshopService workshopService)
|
||||
public EntryInfoViewModel(IRouter router, INotificationService notificationService, IWorkshopService workshopService)
|
||||
{
|
||||
_router = router;
|
||||
_notificationService = notificationService;
|
||||
Entry = entry;
|
||||
UpdatedAt = Entry.Releases.Any() ? Entry.Releases.Max(r => r.CreatedAt) : Entry.CreatedAt;
|
||||
CanBeManaged = Entry.EntryType != EntryType.Profile && workshopService.GetInstalledEntry(entry.Id) != null;
|
||||
_workshopService = workshopService;
|
||||
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
Observable.FromEventPattern<InstalledEntry>(x => workshopService.OnInstalledEntrySaved += x, x => workshopService.OnInstalledEntrySaved -= x)
|
||||
.StartWith([])
|
||||
.Subscribe(_ => CanBeManaged = Entry.EntryType != EntryType.Profile && workshopService.GetInstalledEntry(entry.Id) != null)
|
||||
.Subscribe(_ => CanBeManaged = Entry != null && Entry.EntryType != EntryType.Profile && workshopService.GetInstalledEntry(Entry.Id) != null)
|
||||
.DisposeWith(d);
|
||||
});
|
||||
}
|
||||
|
||||
public IEntryDetails Entry { get; }
|
||||
public DateTimeOffset? UpdatedAt { get; }
|
||||
|
||||
public void SetEntry(IEntryDetails? entry)
|
||||
{
|
||||
Entry = entry;
|
||||
UpdatedAt = Entry != null && Entry.Releases.Any() ? Entry.Releases.Max(r => r.CreatedAt) : Entry?.CreatedAt;
|
||||
CanBeManaged = Entry != null && Entry.EntryType != EntryType.Profile && _workshopService.GetInstalledEntry(Entry.Id) != null;
|
||||
}
|
||||
|
||||
public async Task CopyShareLink()
|
||||
{
|
||||
if (Entry == null)
|
||||
return;
|
||||
|
||||
await Shared.UI.Clipboard.SetTextAsync($"{WorkshopConstants.WORKSHOP_URL}/entries/{Entry.Id}/{StringUtilities.UrlFriendly(Entry.Name)}");
|
||||
_notificationService.CreateNotification().WithTitle("Copied share link to clipboard.").Show();
|
||||
}
|
||||
|
||||
public async Task GoToManage()
|
||||
{
|
||||
await _router.Navigate("/manage");
|
||||
if (Entry == null)
|
||||
return;
|
||||
|
||||
await _router.Navigate($"{Entry.GetEntryPath()}/manage");
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,7 @@ using System.Threading.Tasks;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Routing;
|
||||
using Artemis.WebClient.Workshop;
|
||||
using Artemis.WebClient.Workshop.Extensions;
|
||||
using PropertyChanged.SourceGenerator;
|
||||
using ReactiveUI;
|
||||
|
||||
@ -25,14 +26,15 @@ public partial class EntryReleasesViewModel : ActivatableViewModelBase
|
||||
|
||||
this.WhenActivated(d =>
|
||||
{
|
||||
router.CurrentPath.Subscribe(p => SelectedRelease = p != null && p.Contains("releases") && float.TryParse(p.Split('/').Last(), out float releaseId)
|
||||
? Releases.FirstOrDefault(r => r.Release.Id == releaseId)
|
||||
: null)
|
||||
router.CurrentPath.Subscribe(p =>
|
||||
SelectedRelease = p != null && p.StartsWith(Entry.GetEntryPath()) && float.TryParse(p.Split('/').Last(), out float releaseId)
|
||||
? Releases.FirstOrDefault(r => r.Release.Id == releaseId)
|
||||
: null)
|
||||
.DisposeWith(d);
|
||||
|
||||
this.WhenAnyValue(vm => vm.SelectedRelease)
|
||||
.WhereNotNull()
|
||||
.Subscribe(s => _router.Navigate($"/releases/{s.Release.Id}"))
|
||||
.Subscribe(s => _router.Navigate($"{Entry.GetEntryPath()}/releases/{s.Release.Id}"))
|
||||
.DisposeWith(d);
|
||||
});
|
||||
}
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:layout="clr-namespace:Artemis.UI.Screens.Workshop.Layout"
|
||||
xmlns:mdxaml="https://github.com/whistyun/Markdown.Avalonia.Tight"
|
||||
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:ui="clr-namespace:Artemis.UI"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="800"
|
||||
@ -14,7 +13,7 @@
|
||||
<Border Classes="card" VerticalAlignment="Top">
|
||||
<ContentControl Content="{CompiledBinding EntryInfoViewModel}" />
|
||||
</Border>
|
||||
<Border Classes="card" VerticalAlignment="Top" IsVisible="{CompiledBinding Entry.Releases.Count}">
|
||||
<Border Classes="card" VerticalAlignment="Top" IsVisible="{CompiledBinding Entry.Releases.Count, FallbackValue=False}">
|
||||
<ContentControl Content="{CompiledBinding EntryReleasesViewModel}" />
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
@ -11,7 +11,8 @@ public partial class LayoutDetailsView : ReactiveUserControl<LayoutDetailsViewMo
|
||||
{
|
||||
InitializeComponent();
|
||||
this.WhenActivated(d => ViewModel.WhenAnyValue(vm => vm.Screen)
|
||||
.Subscribe(screen => RouterFrame.NavigateFromObject(screen ?? ViewModel?.LayoutDescriptionViewModel))
|
||||
.WhereNotNull()
|
||||
.Subscribe(screen => RouterFrame.NavigateFromObject(screen))
|
||||
.DisposeWith(d));
|
||||
}
|
||||
}
|
||||
@ -14,31 +14,31 @@ namespace Artemis.UI.Screens.Workshop.Layout;
|
||||
public partial class LayoutDetailsViewModel : RoutableHostScreen<RoutableScreen, WorkshopDetailParameters>
|
||||
{
|
||||
private readonly IWorkshopClient _client;
|
||||
private readonly Func<IEntryDetails, EntryInfoViewModel> _getEntryInfoViewModel;
|
||||
private readonly LayoutDescriptionViewModel _layoutDescriptionViewModel;
|
||||
private readonly Func<IEntryDetails, EntryReleasesViewModel> _getEntryReleasesViewModel;
|
||||
private readonly Func<IEntryDetails, EntryImagesViewModel> _getEntryImagesViewModel;
|
||||
[Notify] private IEntryDetails? _entry;
|
||||
[Notify] private EntryInfoViewModel? _entryInfoViewModel;
|
||||
[Notify] private EntryReleasesViewModel? _entryReleasesViewModel;
|
||||
[Notify] private EntryImagesViewModel? _entryImagesViewModel;
|
||||
|
||||
public LayoutDetailsViewModel(IWorkshopClient client,
|
||||
LayoutDescriptionViewModel layoutDescriptionViewModel,
|
||||
Func<IEntryDetails, EntryInfoViewModel> getEntryInfoViewModel,
|
||||
EntryInfoViewModel entryInfoViewModel,
|
||||
Func<IEntryDetails, EntryReleasesViewModel> getEntryReleasesViewModel,
|
||||
Func<IEntryDetails, EntryImagesViewModel> getEntryImagesViewModel)
|
||||
{
|
||||
_client = client;
|
||||
_getEntryInfoViewModel = getEntryInfoViewModel;
|
||||
_layoutDescriptionViewModel = layoutDescriptionViewModel;
|
||||
_getEntryReleasesViewModel = getEntryReleasesViewModel;
|
||||
_getEntryImagesViewModel = getEntryImagesViewModel;
|
||||
|
||||
LayoutDescriptionViewModel = layoutDescriptionViewModel;
|
||||
RecycleScreen = false;
|
||||
EntryInfoViewModel = entryInfoViewModel;
|
||||
}
|
||||
|
||||
public LayoutDescriptionViewModel LayoutDescriptionViewModel { get; }
|
||||
|
||||
public override RoutableScreen DefaultScreen => _layoutDescriptionViewModel;
|
||||
public EntryInfoViewModel EntryInfoViewModel { get; }
|
||||
|
||||
public override async Task OnNavigating(WorkshopDetailParameters parameters, NavigationArguments args, CancellationToken cancellationToken)
|
||||
{
|
||||
if (Entry?.Id != parameters.EntryId)
|
||||
@ -47,14 +47,18 @@ public partial class LayoutDetailsViewModel : RoutableHostScreen<RoutableScreen,
|
||||
|
||||
private async Task GetEntry(long entryId, CancellationToken cancellationToken)
|
||||
{
|
||||
Task grace = Task.Delay(300, cancellationToken);
|
||||
IOperationResult<IGetEntryByIdResult> result = await _client.GetEntryById.ExecuteAsync(entryId, cancellationToken);
|
||||
if (result.IsErrorResult())
|
||||
return;
|
||||
|
||||
// Let the UI settle to avoid lag when deep linking
|
||||
await grace;
|
||||
|
||||
Entry = result.Data?.Entry;
|
||||
EntryInfoViewModel = Entry != null ? _getEntryInfoViewModel(Entry) : null;
|
||||
EntryInfoViewModel.SetEntry(Entry);
|
||||
EntryReleasesViewModel = Entry != null ? _getEntryReleasesViewModel(Entry) : null;
|
||||
EntryImagesViewModel = Entry != null ? _getEntryImagesViewModel(Entry) : null;
|
||||
LayoutDescriptionViewModel.Entry = Entry;
|
||||
_layoutDescriptionViewModel.Entry = Entry;
|
||||
}
|
||||
}
|
||||
@ -13,7 +13,8 @@ public partial class LayoutListView : ReactiveUserControl<LayoutListViewModel>
|
||||
{
|
||||
InitializeComponent();
|
||||
this.WhenActivated(d => ViewModel.WhenAnyValue(vm => vm.Screen)
|
||||
.Subscribe(screen => RouterFrame.NavigateFromObject(screen ?? ViewModel?.EntryListViewModel))
|
||||
.WhereNotNull()
|
||||
.Subscribe(screen => RouterFrame.NavigateFromObject(screen))
|
||||
.DisposeWith(d));
|
||||
}
|
||||
}
|
||||
@ -6,11 +6,12 @@ namespace Artemis.UI.Screens.Workshop.Layout;
|
||||
|
||||
public class LayoutListViewModel : RoutableHostScreen<RoutableScreen>
|
||||
{
|
||||
public EntryListViewModel EntryListViewModel { get; }
|
||||
private readonly EntryListViewModel _entryListViewModel;
|
||||
public override RoutableScreen DefaultScreen => _entryListViewModel;
|
||||
|
||||
public LayoutListViewModel(EntryListViewModel entryListViewModel)
|
||||
{
|
||||
EntryListViewModel = entryListViewModel;
|
||||
EntryListViewModel.EntryType = EntryType.Layout;
|
||||
_entryListViewModel = entryListViewModel;
|
||||
_entryListViewModel.EntryType = EntryType.Layout;
|
||||
}
|
||||
}
|
||||
@ -13,68 +13,73 @@
|
||||
<UserControl.Resources>
|
||||
<converters:DateTimeConverter x:Key="DateTimeConverter" />
|
||||
</UserControl.Resources>
|
||||
<Grid ColumnDefinitions="300,*" RowDefinitions="*, Auto">
|
||||
<StackPanel Grid.Column="0" Grid.Row="0" Spacing="10" Margin="0 0 10 0">
|
||||
<Border Classes="card" VerticalAlignment="Top">
|
||||
<StackPanel>
|
||||
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}">Management</TextBlock>
|
||||
<Border Classes="card-separator" />
|
||||
<Panel>
|
||||
<ProgressBar HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Top"
|
||||
IsVisible="{CompiledBinding Entry, Converter={x:Static ObjectConverters.IsNull}}"
|
||||
IsIndeterminate="True" />
|
||||
<Grid ColumnDefinitions="300,*" RowDefinitions="*, Auto" IsVisible="{CompiledBinding Entry, Converter={x:Static ObjectConverters.IsNotNull}}">
|
||||
<StackPanel Grid.Column="0" Grid.Row="0" Spacing="10" Margin="0 0 10 0">
|
||||
<Border Classes="card" VerticalAlignment="Top">
|
||||
<StackPanel>
|
||||
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}">Management</TextBlock>
|
||||
<Border Classes="card-separator" />
|
||||
|
||||
<TextBlock Margin="0 0 0 8">
|
||||
<avalonia:MaterialIcon Kind="Downloads" />
|
||||
<Run Classes="h5" Text="{CompiledBinding Entry.Downloads, FallbackValue=0}" />
|
||||
<Run>downloads</Run>
|
||||
</TextBlock>
|
||||
<TextBlock Margin="0 0 0 8">
|
||||
<avalonia:MaterialIcon Kind="Downloads" />
|
||||
<Run Classes="h5" Text="{CompiledBinding Entry.Downloads, FallbackValue=0}" />
|
||||
<Run>downloads</Run>
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock Classes="subtitle" ToolTip.Tip="{CompiledBinding Entry.CreatedAt, Converter={StaticResource DateTimeConverter}}">
|
||||
<avalonia:MaterialIcon Kind="Calendar" />
|
||||
<Run>Created</Run>
|
||||
<Run Text="{CompiledBinding Entry.CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter='humanize'}"></Run>
|
||||
</TextBlock>
|
||||
<TextBlock Classes="subtitle" ToolTip.Tip="{CompiledBinding Entry.CreatedAt, Converter={StaticResource DateTimeConverter}}">
|
||||
<avalonia:MaterialIcon Kind="Calendar" />
|
||||
<Run>Created</Run>
|
||||
<Run Text="{CompiledBinding Entry.CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter='humanize'}"></Run>
|
||||
</TextBlock>
|
||||
|
||||
<Border Classes="card-separator" />
|
||||
<Border Classes="card-separator" />
|
||||
|
||||
<StackPanel Spacing="5">
|
||||
<Button HorizontalAlignment="Stretch" Command="{CompiledBinding CreateRelease}">
|
||||
Create new release
|
||||
</Button>
|
||||
<Button Classes="danger" HorizontalAlignment="Stretch" Command="{CompiledBinding DeleteSubmission}">
|
||||
Delete submission
|
||||
</Button>
|
||||
<StackPanel Spacing="5">
|
||||
<Button HorizontalAlignment="Stretch" Command="{CompiledBinding CreateRelease}">
|
||||
Create new release
|
||||
</Button>
|
||||
<Button Classes="danger" HorizontalAlignment="Stretch" Command="{CompiledBinding DeleteSubmission}">
|
||||
Delete submission
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<Border Classes="card" IsVisible="{CompiledBinding Releases.Count}">
|
||||
<StackPanel>
|
||||
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}">Releases</TextBlock>
|
||||
<Border Classes="card-separator" />
|
||||
</Border>
|
||||
<Border Classes="card" IsVisible="{CompiledBinding Releases.Count}">
|
||||
<StackPanel>
|
||||
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}">Releases</TextBlock>
|
||||
<Border Classes="card-separator" />
|
||||
|
||||
<ListBox ItemsSource="{CompiledBinding Releases}" SelectedItem="{CompiledBinding SelectedRelease}">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Margin="0 5">
|
||||
<TextBlock Text="{CompiledBinding 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>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<controls:HyperlinkButton Command="{CompiledBinding ViewWorkshopPage}" HorizontalAlignment="Center">
|
||||
View workshop page
|
||||
</controls:HyperlinkButton>
|
||||
</StackPanel>
|
||||
|
||||
<controls:Frame Grid.Column="1" Grid.Row="0" Name="RouterFrame" IsNavigationStackEnabled="False" CacheSize="0">
|
||||
<controls:Frame.NavigationPageFactory>
|
||||
<ui:PageFactory />
|
||||
</controls:Frame.NavigationPageFactory>
|
||||
</controls:Frame>
|
||||
</Grid>
|
||||
<ListBox ItemsSource="{CompiledBinding Releases}" SelectedItem="{CompiledBinding SelectedRelease}">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Margin="0 5">
|
||||
<TextBlock Text="{CompiledBinding 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>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<controls:HyperlinkButton Command="{CompiledBinding ViewWorkshopPage}" HorizontalAlignment="Center">
|
||||
View workshop page
|
||||
</controls:HyperlinkButton>
|
||||
</StackPanel>
|
||||
|
||||
<controls:Frame Grid.Column="1" Grid.Row="0" Name="RouterFrame" IsNavigationStackEnabled="False" CacheSize="0">
|
||||
<controls:Frame.NavigationPageFactory>
|
||||
<ui:PageFactory />
|
||||
</controls:Frame.NavigationPageFactory>
|
||||
</controls:Frame>
|
||||
</Grid>
|
||||
</Panel>
|
||||
</UserControl>
|
||||
@ -11,7 +11,8 @@ public partial class SubmissionManagementView : ReactiveUserControl<SubmissionMa
|
||||
{
|
||||
InitializeComponent();
|
||||
this.WhenActivated(d => ViewModel.WhenAnyValue(vm => vm.Screen)
|
||||
.Subscribe(screen => RouterFrame.NavigateFromObject(screen ?? ViewModel?.DetailsViewModel))
|
||||
.WhereNotNull()
|
||||
.Subscribe(screen => RouterFrame.NavigateFromObject(screen))
|
||||
.DisposeWith(d));
|
||||
}
|
||||
}
|
||||
@ -22,6 +22,7 @@ public partial class SubmissionManagementViewModel : RoutableHostScreen<Routable
|
||||
private readonly IWindowService _windowService;
|
||||
private readonly IRouter _router;
|
||||
private readonly IWorkshopService _workshopService;
|
||||
private readonly SubmissionDetailsViewModel _detailsViewModel;
|
||||
|
||||
[Notify] private IGetSubmittedEntryById_Entry? _entry;
|
||||
[Notify] private List<IGetSubmittedEntryById_Entry_Releases>? _releases;
|
||||
@ -29,7 +30,7 @@ public partial class SubmissionManagementViewModel : RoutableHostScreen<Routable
|
||||
|
||||
public SubmissionManagementViewModel(IWorkshopClient client, IRouter router, IWindowService windowService, IWorkshopService workshopService, SubmissionDetailsViewModel detailsViewModel)
|
||||
{
|
||||
DetailsViewModel = detailsViewModel;
|
||||
_detailsViewModel = detailsViewModel;
|
||||
_client = client;
|
||||
_router = router;
|
||||
_windowService = windowService;
|
||||
@ -39,12 +40,12 @@ public partial class SubmissionManagementViewModel : RoutableHostScreen<Routable
|
||||
{
|
||||
this.WhenAnyValue(vm => vm.SelectedRelease)
|
||||
.WhereNotNull()
|
||||
.Subscribe(r => _router.Navigate($"/releases/{r.Id}"))
|
||||
.Subscribe(r => _router.Navigate($"workshop/library/submissions/{Entry?.Id}/releases/{r.Id}"))
|
||||
.DisposeWith(d);
|
||||
});
|
||||
}
|
||||
|
||||
public SubmissionDetailsViewModel DetailsViewModel { get; }
|
||||
public override RoutableScreen DefaultScreen => _detailsViewModel;
|
||||
|
||||
public async Task ViewWorkshopPage()
|
||||
{
|
||||
@ -79,7 +80,7 @@ public partial class SubmissionManagementViewModel : RoutableHostScreen<Routable
|
||||
{
|
||||
// If there is a 2nd parameter, it's a release ID
|
||||
SelectedRelease = args.RouteParameters.Length > 1 ? Releases?.FirstOrDefault(r => r.Id == (long) args.RouteParameters[1]) : null;
|
||||
|
||||
|
||||
IOperationResult<IGetSubmittedEntryByIdResult> result = await _client.GetSubmittedEntryById.ExecuteAsync(parameters.EntryId, cancellationToken);
|
||||
if (result.IsErrorResult())
|
||||
return;
|
||||
@ -87,11 +88,11 @@ public partial class SubmissionManagementViewModel : RoutableHostScreen<Routable
|
||||
Entry = result.Data?.Entry;
|
||||
Releases = Entry?.Releases.OrderByDescending(r => r.CreatedAt).ToList();
|
||||
|
||||
await DetailsViewModel.SetEntry(Entry, cancellationToken);
|
||||
await _detailsViewModel.SetEntry(Entry, cancellationToken);
|
||||
}
|
||||
|
||||
public override async Task OnClosing(NavigationArguments args)
|
||||
{
|
||||
await DetailsViewModel.OnClosing(args);
|
||||
await _detailsViewModel.OnClosing(args);
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,6 @@
|
||||
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:mdxaml="https://github.com/whistyun/Markdown.Avalonia.Tight"
|
||||
xmlns:plugins="clr-namespace:Artemis.UI.Screens.Workshop.Plugins"
|
||||
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
@ -17,21 +16,32 @@
|
||||
</Border>
|
||||
|
||||
<Border Classes="card" VerticalAlignment="Top" IsVisible="{CompiledBinding Entry.PluginInfo, Converter={x:Static ObjectConverters.IsNotNull}}">
|
||||
<StackPanel>
|
||||
<TextBlock>Admin required</TextBlock>
|
||||
<TextBlock Text="Yes" IsVisible="{CompiledBinding Entry.PluginInfo.RequiresAdmin}" />
|
||||
<TextBlock Text="No" IsVisible="{CompiledBinding !Entry.PluginInfo.RequiresAdmin}" />
|
||||
|
||||
<TextBlock Margin="0 15 0 5">Supported platforms</TextBlock>
|
||||
<StackPanel Orientation="Horizontal" Spacing="10">
|
||||
<avalonia:MaterialIcon Kind="MicrosoftWindows" IsVisible="{CompiledBinding Entry.PluginInfo.SupportsWindows}" />
|
||||
<avalonia:MaterialIcon Kind="Linux" IsVisible="{CompiledBinding Entry.PluginInfo.SupportsLinux}" />
|
||||
<avalonia:MaterialIcon Kind="Apple" IsVisible="{CompiledBinding Entry.PluginInfo.SupportsOSX}" />
|
||||
<Panel>
|
||||
<StackPanel IsVisible="{CompiledBinding Entry, Converter={x:Static ObjectConverters.IsNull}}">
|
||||
<Border Width="110" Classes="skeleton-text"></Border>
|
||||
<Border Width="35" Classes="skeleton-text"></Border>
|
||||
|
||||
<Border Margin="0 16 0 3" Width="130" Classes="skeleton-text"></Border>
|
||||
<Border Width="60" Classes="skeleton-text"></Border>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel IsVisible="{CompiledBinding Entry, Converter={x:Static ObjectConverters.IsNotNull}}">
|
||||
<TextBlock>Admin required</TextBlock>
|
||||
<TextBlock Text="Yes" IsVisible="{CompiledBinding Entry.PluginInfo.RequiresAdmin}" />
|
||||
<TextBlock Text="No" IsVisible="{CompiledBinding !Entry.PluginInfo.RequiresAdmin}" />
|
||||
|
||||
<TextBlock Margin="0 15 0 5">Supported platforms</TextBlock>
|
||||
<StackPanel Orientation="Horizontal" Spacing="10">
|
||||
<avalonia:MaterialIcon Kind="MicrosoftWindows" IsVisible="{CompiledBinding Entry.PluginInfo.SupportsWindows}" />
|
||||
<avalonia:MaterialIcon Kind="Linux" IsVisible="{CompiledBinding Entry.PluginInfo.SupportsLinux}" />
|
||||
<avalonia:MaterialIcon Kind="Apple" IsVisible="{CompiledBinding Entry.PluginInfo.SupportsOSX}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Panel>
|
||||
|
||||
</Border>
|
||||
|
||||
<Border Classes="card" VerticalAlignment="Top" IsVisible="{CompiledBinding Entry.Releases.Count}">
|
||||
<Border Classes="card" VerticalAlignment="Top" IsVisible="{CompiledBinding Entry.Releases.Count, FallbackValue=False}">
|
||||
<ContentControl Content="{CompiledBinding EntryReleasesViewModel}" />
|
||||
</Border>
|
||||
</StackPanel>
|
||||
@ -44,6 +54,6 @@
|
||||
</controls:Frame>
|
||||
</ScrollViewer>
|
||||
|
||||
<ContentControl Grid.Row="1" Grid.Column="2" IsVisible="{CompiledBinding Entry.Images.Count}" Content="{CompiledBinding EntryImagesViewModel}" />
|
||||
<ContentControl Grid.Row="1" Grid.Column="2" IsVisible="{CompiledBinding Entry.Images.Count, FallbackValue=False}" Content="{CompiledBinding EntryImagesViewModel}" />
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@ -11,7 +11,8 @@ public partial class PluginDetailsView : ReactiveUserControl<PluginDetailsViewMo
|
||||
{
|
||||
InitializeComponent();
|
||||
this.WhenActivated(d => ViewModel.WhenAnyValue(vm => vm.Screen)
|
||||
.Subscribe(screen => RouterFrame.NavigateFromObject(screen ?? ViewModel?.PluginDescriptionViewModel))
|
||||
.WhereNotNull()
|
||||
.Subscribe(screen => RouterFrame.NavigateFromObject(screen))
|
||||
.DisposeWith(d));
|
||||
}
|
||||
}
|
||||
@ -16,32 +16,32 @@ namespace Artemis.UI.Screens.Workshop.Plugins;
|
||||
public partial class PluginDetailsViewModel : RoutableHostScreen<RoutableScreen, WorkshopDetailParameters>
|
||||
{
|
||||
private readonly IWorkshopClient _client;
|
||||
private readonly Func<IEntryDetails, EntryInfoViewModel> _getEntryInfoViewModel;
|
||||
private readonly PluginDescriptionViewModel _pluginDescriptionViewModel;
|
||||
private readonly Func<IEntryDetails, EntryReleasesViewModel> _getEntryReleasesViewModel;
|
||||
private readonly Func<IEntryDetails, EntryImagesViewModel> _getEntryImagesViewModel;
|
||||
[Notify] private IGetPluginEntryById_Entry? _entry;
|
||||
[Notify] private EntryInfoViewModel? _entryInfoViewModel;
|
||||
[Notify] private EntryReleasesViewModel? _entryReleasesViewModel;
|
||||
[Notify] private EntryImagesViewModel? _entryImagesViewModel;
|
||||
[Notify] private ReadOnlyObservableCollection<EntryListItemViewModel>? _dependants;
|
||||
|
||||
public PluginDetailsViewModel(IWorkshopClient client,
|
||||
PluginDescriptionViewModel pluginDescriptionViewModel,
|
||||
Func<IEntryDetails, EntryInfoViewModel> getEntryInfoViewModel,
|
||||
EntryInfoViewModel entryInfoViewModel,
|
||||
Func<IEntryDetails, EntryReleasesViewModel> getEntryReleasesViewModel,
|
||||
Func<IEntryDetails, EntryImagesViewModel> getEntryImagesViewModel)
|
||||
{
|
||||
_client = client;
|
||||
_getEntryInfoViewModel = getEntryInfoViewModel;
|
||||
_pluginDescriptionViewModel = pluginDescriptionViewModel;
|
||||
_getEntryReleasesViewModel = getEntryReleasesViewModel;
|
||||
_getEntryImagesViewModel = getEntryImagesViewModel;
|
||||
|
||||
PluginDescriptionViewModel = pluginDescriptionViewModel;
|
||||
|
||||
EntryInfoViewModel = entryInfoViewModel;
|
||||
RecycleScreen = false;
|
||||
}
|
||||
|
||||
public PluginDescriptionViewModel PluginDescriptionViewModel { get; }
|
||||
|
||||
public override RoutableScreen DefaultScreen => _pluginDescriptionViewModel;
|
||||
public EntryInfoViewModel EntryInfoViewModel { get; }
|
||||
|
||||
public override async Task OnNavigating(WorkshopDetailParameters parameters, NavigationArguments args, CancellationToken cancellationToken)
|
||||
{
|
||||
if (Entry?.Id != parameters.EntryId)
|
||||
@ -50,15 +50,19 @@ public partial class PluginDetailsViewModel : RoutableHostScreen<RoutableScreen,
|
||||
|
||||
private async Task GetEntry(long entryId, CancellationToken cancellationToken)
|
||||
{
|
||||
Task grace = Task.Delay(300, cancellationToken);
|
||||
IOperationResult<IGetPluginEntryByIdResult> result = await _client.GetPluginEntryById.ExecuteAsync(entryId, cancellationToken);
|
||||
if (result.IsErrorResult())
|
||||
return;
|
||||
|
||||
// Let the UI settle to avoid lag when deep linking
|
||||
await grace;
|
||||
|
||||
Entry = result.Data?.Entry;
|
||||
EntryInfoViewModel = Entry != null ? _getEntryInfoViewModel(Entry) : null;
|
||||
EntryInfoViewModel.SetEntry(Entry);
|
||||
EntryReleasesViewModel = Entry != null ? _getEntryReleasesViewModel(Entry) : null;
|
||||
EntryImagesViewModel = Entry != null ? _getEntryImagesViewModel(Entry) : null;
|
||||
|
||||
await PluginDescriptionViewModel.SetEntry(Entry, cancellationToken);
|
||||
await _pluginDescriptionViewModel.SetEntry(Entry, cancellationToken);
|
||||
}
|
||||
}
|
||||
@ -11,7 +11,8 @@ public partial class PluginListView : ReactiveUserControl<PluginListViewModel>
|
||||
{
|
||||
InitializeComponent();
|
||||
this.WhenActivated(d => ViewModel.WhenAnyValue(vm => vm.Screen)
|
||||
.Subscribe(screen => RouterFrame.NavigateFromObject(screen ?? ViewModel?.EntryListViewModel))
|
||||
.WhereNotNull()
|
||||
.Subscribe(screen => RouterFrame.NavigateFromObject(screen))
|
||||
.DisposeWith(d));
|
||||
}
|
||||
}
|
||||
@ -6,11 +6,12 @@ namespace Artemis.UI.Screens.Workshop.Plugins;
|
||||
|
||||
public class PluginListViewModel : RoutableHostScreen<RoutableScreen>
|
||||
{
|
||||
public EntryListViewModel EntryListViewModel { get; }
|
||||
private readonly EntryListViewModel _entryListViewModel;
|
||||
public override RoutableScreen DefaultScreen => _entryListViewModel;
|
||||
|
||||
public PluginListViewModel(EntryListViewModel entryListViewModel)
|
||||
{
|
||||
EntryListViewModel = entryListViewModel;
|
||||
EntryListViewModel.EntryType = EntryType.Plugin;
|
||||
_entryListViewModel = entryListViewModel;
|
||||
_entryListViewModel.EntryType = EntryType.Plugin;
|
||||
}
|
||||
}
|
||||
@ -3,7 +3,6 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:profile="clr-namespace:Artemis.UI.Screens.Workshop.Profile"
|
||||
xmlns:mdxaml="https://github.com/whistyun/Markdown.Avalonia.Tight"
|
||||
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:ui="clr-namespace:Artemis.UI"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="800"
|
||||
@ -14,7 +13,7 @@
|
||||
<Border Classes="card" VerticalAlignment="Top">
|
||||
<ContentControl Content="{CompiledBinding EntryInfoViewModel}" />
|
||||
</Border>
|
||||
<Border Classes="card" VerticalAlignment="Top" IsVisible="{CompiledBinding Entry.Releases.Count}">
|
||||
<Border Classes="card" VerticalAlignment="Top" IsVisible="{CompiledBinding Entry.Releases.Count, FallbackValue=False}">
|
||||
<ContentControl Content="{CompiledBinding EntryReleasesViewModel}" />
|
||||
</Border>
|
||||
</StackPanel>
|
||||
@ -27,6 +26,6 @@
|
||||
</controls:Frame>
|
||||
</ScrollViewer>
|
||||
|
||||
<ContentControl Grid.Row="1" Grid.Column="2" IsVisible="{CompiledBinding Entry.Images.Count}" Content="{CompiledBinding EntryImagesViewModel}" />
|
||||
<ContentControl Grid.Row="1" Grid.Column="2" IsVisible="{CompiledBinding Entry.Images.Count, FallbackValue=False}" Content="{CompiledBinding EntryImagesViewModel}" />
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@ -11,7 +11,8 @@ public partial class ProfileDetailsView : ReactiveUserControl<ProfileDetailsView
|
||||
{
|
||||
InitializeComponent();
|
||||
this.WhenActivated(d => ViewModel.WhenAnyValue(vm => vm.Screen)
|
||||
.Subscribe(screen => RouterFrame.NavigateFromObject(screen ?? ViewModel?.ProfileDescriptionViewModel))
|
||||
.WhereNotNull()
|
||||
.Subscribe(screen => RouterFrame.NavigateFromObject(screen))
|
||||
.DisposeWith(d));
|
||||
}
|
||||
}
|
||||
@ -17,32 +17,32 @@ namespace Artemis.UI.Screens.Workshop.Profile;
|
||||
public partial class ProfileDetailsViewModel : RoutableHostScreen<RoutableScreen, WorkshopDetailParameters>
|
||||
{
|
||||
private readonly IWorkshopClient _client;
|
||||
private readonly Func<IEntryDetails, EntryInfoViewModel> _getEntryInfoViewModel;
|
||||
private readonly ProfileDescriptionViewModel _profileDescriptionViewModel;
|
||||
private readonly Func<IEntryDetails, EntryReleasesViewModel> _getEntryReleasesViewModel;
|
||||
private readonly Func<IEntryDetails, EntryImagesViewModel> _getEntryImagesViewModel;
|
||||
|
||||
[Notify] private IEntryDetails? _entry;
|
||||
[Notify] private EntryInfoViewModel? _entryInfoViewModel;
|
||||
[Notify] private EntryReleasesViewModel? _entryReleasesViewModel;
|
||||
[Notify] private EntryImagesViewModel? _entryImagesViewModel;
|
||||
|
||||
public ProfileDetailsViewModel(IWorkshopClient client,
|
||||
ProfileDescriptionViewModel profileDescriptionViewModel,
|
||||
Func<IEntryDetails, EntryInfoViewModel> getEntryInfoViewModel,
|
||||
EntryInfoViewModel entryInfoViewModel,
|
||||
Func<IEntryDetails, EntryReleasesViewModel> getEntryReleasesViewModel,
|
||||
Func<IEntryDetails, EntryImagesViewModel> getEntryImagesViewModel)
|
||||
{
|
||||
_client = client;
|
||||
_getEntryInfoViewModel = getEntryInfoViewModel;
|
||||
_profileDescriptionViewModel = profileDescriptionViewModel;
|
||||
_getEntryReleasesViewModel = getEntryReleasesViewModel;
|
||||
_getEntryImagesViewModel = getEntryImagesViewModel;
|
||||
|
||||
ProfileDescriptionViewModel = profileDescriptionViewModel;
|
||||
EntryInfoViewModel = entryInfoViewModel;
|
||||
RecycleScreen = false;
|
||||
}
|
||||
|
||||
public ProfileDescriptionViewModel ProfileDescriptionViewModel { get; }
|
||||
|
||||
public override RoutableScreen DefaultScreen => _profileDescriptionViewModel;
|
||||
public EntryInfoViewModel EntryInfoViewModel { get; }
|
||||
|
||||
public override async Task OnNavigating(WorkshopDetailParameters parameters, NavigationArguments args, CancellationToken cancellationToken)
|
||||
{
|
||||
if (Entry?.Id != parameters.EntryId)
|
||||
@ -51,15 +51,19 @@ public partial class ProfileDetailsViewModel : RoutableHostScreen<RoutableScreen
|
||||
|
||||
private async Task GetEntry(long entryId, CancellationToken cancellationToken)
|
||||
{
|
||||
Task grace = Task.Delay(300, cancellationToken);
|
||||
IOperationResult<IGetEntryByIdResult> result = await _client.GetEntryById.ExecuteAsync(entryId, cancellationToken);
|
||||
if (result.IsErrorResult())
|
||||
return;
|
||||
|
||||
// Let the UI settle to avoid lag when deep linking
|
||||
await grace;
|
||||
|
||||
Entry = result.Data?.Entry;
|
||||
EntryInfoViewModel = Entry != null ? _getEntryInfoViewModel(Entry) : null;
|
||||
EntryInfoViewModel.SetEntry(Entry);
|
||||
EntryReleasesViewModel = Entry != null ? _getEntryReleasesViewModel(Entry) : null;
|
||||
EntryImagesViewModel = Entry != null ? _getEntryImagesViewModel(Entry) : null;
|
||||
|
||||
await ProfileDescriptionViewModel.SetEntry(Entry, cancellationToken);
|
||||
await _profileDescriptionViewModel.SetEntry(Entry, cancellationToken);
|
||||
}
|
||||
}
|
||||
@ -11,7 +11,8 @@ public partial class ProfileListView : ReactiveUserControl<ProfileListViewModel>
|
||||
{
|
||||
InitializeComponent();
|
||||
this.WhenActivated(d => ViewModel.WhenAnyValue(vm => vm.Screen)
|
||||
.Subscribe(screen => RouterFrame.NavigateFromObject(screen ?? ViewModel?.EntryListViewModel))
|
||||
.WhereNotNull()
|
||||
.Subscribe(screen => RouterFrame.NavigateFromObject(screen))
|
||||
.DisposeWith(d));
|
||||
}
|
||||
}
|
||||
@ -6,11 +6,12 @@ namespace Artemis.UI.Screens.Workshop.Profile;
|
||||
|
||||
public class ProfileListViewModel : RoutableHostScreen<RoutableScreen>
|
||||
{
|
||||
public EntryListViewModel EntryListViewModel { get; }
|
||||
private readonly EntryListViewModel _entryListViewModel;
|
||||
public override RoutableScreen DefaultScreen => _entryListViewModel;
|
||||
|
||||
public ProfileListViewModel(EntryListViewModel entryListViewModel)
|
||||
{
|
||||
EntryListViewModel = entryListViewModel;
|
||||
EntryListViewModel.EntryType = EntryType.Profile;
|
||||
_entryListViewModel = entryListViewModel;
|
||||
_entryListViewModel.EntryType = EntryType.Profile;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
namespace Artemis.WebClient.Workshop.Extensions;
|
||||
|
||||
public static class EntryExtensions
|
||||
{
|
||||
public static string GetEntryPath(this IEntryDetails entry)
|
||||
{
|
||||
return $"workshop/entries/{entry.EntryType.ToString().ToLower()}s/details/{entry.Id}";
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user