Skip to content

Commit

Permalink
Merge pull request #2170 from erri120/feat/collection-download-page
Browse files Browse the repository at this point in the history
Collection download page
  • Loading branch information
erri120 authored Oct 15, 2024
2 parents 9d425b4 + 33720e1 commit cce17a3
Show file tree
Hide file tree
Showing 19 changed files with 436 additions and 139 deletions.
Binary file added src/NexusMods.App.UI/Assets/black-box.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 12 additions & 1 deletion src/NexusMods.App.UI/ImagePipelines.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using NexusMods.Hashing.xxHash64;
using NexusMods.Media;
using NexusMods.MnemonicDB.Abstractions;
using R3;

namespace NexusMods.App.UI;

Expand All @@ -19,6 +20,16 @@ internal static class ImagePipelines
private const string CollectionBackgroundImagePipelineKey = nameof(CollectionBackgroundImagePipelineKey);

private static readonly Bitmap CollectionTileFallback = new(AssetLoader.Open(new Uri("avares://NexusMods.App.UI/Assets/collection-tile-fallback.png")));
private static readonly Bitmap CollectionBackgroundFallback = new(AssetLoader.Open(new Uri("avares://NexusMods.App.UI/Assets/black-box.png")));

public static Observable<Bitmap> CreateObservable(EntityId input, IResourceLoader<EntityId, Bitmap> pipeline)
{
return Observable
.Return(input)
.ObserveOnThreadPool()
.SelectAwait(async (id, cancellationToken) => await pipeline.LoadResourceAsync(id, cancellationToken), configureAwait: false)
.Select(static resource => resource.Data);
}

public static IServiceCollection AddImagePipelines(this IServiceCollection serviceCollection)
{
Expand Down Expand Up @@ -82,7 +93,7 @@ private static IResourceLoader<EntityId, Bitmap> CreateCollectionBackgroundImage
)
.Decode(decoderType: DecoderType.Skia)
.ToAvaloniaBitmap()
// TODO: .UseFallbackValue()
.UseFallbackValue(CollectionBackgroundFallback)
.EntityIdToIdentifier(
connection: connection,
attribute: CollectionMetadata.BackgroundImageUri
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@
using Avalonia.Platform;
using NexusMods.Abstractions.Jobs;
using NexusMods.Abstractions.NexusWebApi.Types;
using NexusMods.App.UI.Windows;
using NexusMods.App.UI.WorkspaceSystem;
using NexusMods.Paths;

namespace NexusMods.App.UI.Pages.CollectionDownload;

public class CollectionDownloadDesignViewModel : AViewModel<ICollectionDownloadViewModel>, ICollectionDownloadViewModel
public class CollectionDownloadDesignViewModel : APageViewModel<ICollectionDownloadViewModel>, ICollectionDownloadViewModel
{
public CollectionDownloadDesignViewModel() : base(new DesignWindowManager()) { }

public string Name => "Vanilla+ [Quality of Life]";
public CollectionSlug Slug { get; } = CollectionSlug.From("tckf0m");
public RevisionNumber RevisionNumber { get; } = RevisionNumber.From(6);
Expand All @@ -19,13 +23,11 @@ public class CollectionDownloadDesignViewModel : AViewModel<ICollectionDownloadV
public int ModCount => 9;
public int RequiredModCount => 7;
public int OptionalModCount => 2;
public int EndorsementCount => 248;
public int DownloadCount => 35_123;
public ulong EndorsementCount => 248;
public ulong DownloadCount => 35_123;
public Size TotalSize { get; } = Size.From(76_123_456);
public Percent OverallRating { get; } = Percent.CreateClamped(0.82);
public Bitmap TileImage { get; } = new(AssetLoader.Open(new Uri("avares://NexusMods.App.UI/Assets/DesignTime/collection_tile_image.png")));

public Bitmap BackgroundImage { get; } = new(AssetLoader.Open(new Uri("avares://NexusMods.App.UI/Assets/DesignTime/header-background.webp")));

public string CollectionStatusText { get; } = "0 of 9 mods downloaded";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using NexusMods.Abstractions.NexusModsLibrary.Models;
using NexusMods.Abstractions.Serialization.Attributes;
using NexusMods.App.UI.WorkspaceSystem;
using NexusMods.MnemonicDB.Abstractions;

namespace NexusMods.App.UI.Pages.CollectionDownload;

[JsonName(nameof(CollectionDownloadPageContext))]
public record CollectionDownloadPageContext : IPageFactoryContext
{
public required CollectionRevisionMetadataId CollectionRevisionMetadataId { get; init; }
}

[UsedImplicitly]
public class CollectionDownloadPageFactory : APageFactory<ICollectionDownloadViewModel, CollectionDownloadPageContext>
{
public static readonly PageFactoryId StaticId = PageFactoryId.From(Guid.Parse("50790b33-41cb-432e-a877-4730d2b3c13e"));

private readonly IConnection _connection;
public CollectionDownloadPageFactory(IServiceProvider serviceProvider) : base(serviceProvider)
{
_connection = serviceProvider.GetRequiredService<IConnection>();
}

public override PageFactoryId Id => StaticId;

public override ICollectionDownloadViewModel CreateViewModel(CollectionDownloadPageContext context)
{
var metadata = CollectionRevisionMetadata.Load(_connection.Db, context.CollectionRevisionMetadataId);
if (!metadata.IsValid()) throw new InvalidOperationException($"{nameof(CollectionRevisionMetadata)} is invalid for `{context.CollectionRevisionMetadataId}`");

return new CollectionDownloadViewModel(
windowManager: WindowManager,
tileImagePipeline: ImagePipelines.GetCollectionTileImagePipeline(ServiceProvider),
backgroundImagePipeline: ImagePipelines.GetCollectionBackgroundImagePipeline(ServiceProvider),
revisionMetadata: metadata
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@

<panels:FlexPanel x:Name="Body">


<Border x:Name="HeaderBorderBackground">
<Border x:Name="HeaderBorder">
<panels:FlexPanel x:Name="Header">

Expand Down Expand Up @@ -104,6 +104,7 @@
</Border>
</panels:FlexPanel>
</Border>
</Border>

<Border x:Name="ListHeaderRowBorder">
<panels:FlexPanel x:Name="ListHeaderRow">
Expand All @@ -114,26 +115,33 @@
</panels:FlexPanel>
</Border>

<TabControl>
<TabItem>
<TabItem.Header>
<panels:FlexPanel x:Name="RequiredModsFlexPanel">
<TextBlock>Required</TextBlock>
<Border>
<TextBlock x:Name="RequiredModsCount" />
</Border>
</panels:FlexPanel>
</TabItem.Header>
<!-- TreeDataGrid -->
<TextBlock>TODO: Required mods will appear here</TextBlock>
</TabItem>
<TabItem>
<TabItem.Header>
<panels:FlexPanel x:Name="OptionalModsFlexPanel">
<TextBlock>Optional</TextBlock>
<Border>
<TextBlock x:Name="OptionalModsCount" />
</Border>
</panels:FlexPanel>
</TabItem.Header>
<!-- TreeDataGrid -->
<TextBlock>TODO: Optional mods will appear here</TextBlock>
</TabItem>
</TabControl>

<!-- not touching these for now -->
<Expander>
<Expander.Header>
<panels:FlexPanel>
<TextBlock>Required</TextBlock>
<TextBlock x:Name="RequiredModsCount" />
</panels:FlexPanel>
</Expander.Header>
<TextBlock>TODO: Grid of mods will appear here</TextBlock>
</Expander>
<Expander>
<Expander.Header>
<panels:FlexPanel>
<TextBlock>Optional</TextBlock>
<TextBlock x:Name="OptionalModsCount" />
</panels:FlexPanel>
</Expander.Header>
<TextBlock>TODO: Grid of mods will appear here</TextBlock>
</Expander>
</panels:FlexPanel>

</reactiveUi:ReactiveUserControl>
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public CollectionDownloadView()
// Uncomment this to enable the background image
this.WhenAnyValue(view => view.ViewModel!.BackgroundImage)
.WhereNotNull()
.SubscribeWithErrorLogging(image => Body.Background = new ImageBrush { Source = image, Stretch = Stretch.UniformToFill})
.SubscribeWithErrorLogging(image => HeaderBorderBackground.Background = new ImageBrush { Source = image, Stretch = Stretch.UniformToFill, AlignmentY = AlignmentY.Top})
.DisposeWith(d);

this.WhenAnyValue(view => view.ViewModel!.TileImage)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using Avalonia.Media.Imaging;
using NexusMods.Abstractions.Jobs;
using NexusMods.Abstractions.NexusModsLibrary.Models;
using NexusMods.Abstractions.NexusWebApi.Types;
using NexusMods.Abstractions.Resources;
using NexusMods.App.UI.Windows;
using NexusMods.App.UI.WorkspaceSystem;
using NexusMods.MnemonicDB.Abstractions;
using NexusMods.Paths;
using R3;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;

namespace NexusMods.App.UI.Pages.CollectionDownload;

public class CollectionDownloadViewModel : APageViewModel<ICollectionDownloadViewModel>, ICollectionDownloadViewModel
{
private readonly CollectionRevisionMetadata.ReadOnly _revision;
private readonly CollectionMetadata.ReadOnly _collection;

public CollectionDownloadViewModel(
IWindowManager windowManager,
IResourceLoader<EntityId, Bitmap> tileImagePipeline,
IResourceLoader<EntityId, Bitmap> backgroundImagePipeline,
CollectionRevisionMetadata.ReadOnly revisionMetadata) : base(windowManager)
{
_revision = revisionMetadata;
_collection = revisionMetadata.Collection;

// TODO:
CollectionStatusText = "TODO";

var requiredModCount = 0;
var optionalModCount = 0;
foreach (var file in _revision.Files)
{
var isOptional = file.IsOptional;

requiredModCount += isOptional ? 0 : 1;
optionalModCount += isOptional ? 1 : 0;
}

RequiredModCount = requiredModCount;
OptionalModCount = optionalModCount;

this.WhenActivated(disposables =>
{
ImagePipelines.CreateObservable(_collection.Id, tileImagePipeline)
.ObserveOnUIThreadDispatcher()
.Subscribe(this, static (bitmap, self) => self.TileImage = bitmap)
.AddTo(disposables);

ImagePipelines.CreateObservable(_collection.Id, backgroundImagePipeline)
.ObserveOnUIThreadDispatcher()
.Subscribe(this, static (bitmap, self) => self.BackgroundImage = bitmap)
.AddTo(disposables);
});
}

public string Name => _collection.Name;
public string Summary => _collection.Summary;
public int ModCount => _revision.Files.Count;
public ulong EndorsementCount => _collection.Endorsements;
public ulong DownloadCount => _revision.Downloads;
public Size TotalSize => _revision.TotalSize;
public Percent OverallRating => Percent.CreateClamped(_revision.OverallRating);
public string AuthorName => _collection.Author.Name;

public CollectionSlug Slug => _collection.Slug;
public RevisionNumber RevisionNumber => _revision.RevisionNumber;
public int RequiredModCount { get; }
public int OptionalModCount { get; }

[Reactive] public Bitmap? TileImage { get; private set; }
[Reactive] public Bitmap? BackgroundImage { get; private set; }
[Reactive] public string CollectionStatusText { get; private set; }
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
using Avalonia.Media.Imaging;
using NexusMods.Abstractions.Jobs;
using NexusMods.Abstractions.NexusWebApi.Types;
using NexusMods.App.UI.WorkspaceSystem;
using NexusMods.Paths;

namespace NexusMods.App.UI.Pages.CollectionDownload;

public interface ICollectionDownloadViewModel : IViewModelInterface
public interface ICollectionDownloadViewModel : IPageViewModelInterface
{
/// <summary>
/// Name of the collection
Expand Down Expand Up @@ -50,12 +51,12 @@ public interface ICollectionDownloadViewModel : IViewModelInterface
/// <summary>
/// The number of endorsements the collection has
/// </summary>
public int EndorsementCount { get; }
public ulong EndorsementCount { get; }

/// <summary>
/// The number of downloads the collection has
/// </summary>
public int DownloadCount { get; }
public ulong DownloadCount { get; }

/// <summary>
/// The size of the collection including all downloads and the collection file iteself
Expand All @@ -70,12 +71,12 @@ public interface ICollectionDownloadViewModel : IViewModelInterface
/// <summary>
/// The small tileable image of the collection
/// </summary>
public Bitmap TileImage { get; }
public Bitmap? TileImage { get; }

/// <summary>
/// The background banner image of the collection
/// </summary>
public Bitmap BackgroundImage { get; }
public Bitmap? BackgroundImage { get; }

/// <summary>
/// A text representation of the collection's status, such as "Downloading", "Installing", "Ready to Play", etc.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using NexusMods.Abstractions.Jobs;
using NexusMods.App.UI.Controls.Navigation;
using NexusMods.Paths;

namespace NexusMods.App.UI.Pages.LibraryPage.Collections;
Expand All @@ -18,4 +19,5 @@ public class CollectionCardDesignViewModel : AViewModel<ICollectionCardViewModel
public Percent OverallRating => Percent.CreateClamped(0.84);
public string AuthorName => "FantasyAuthor";
public Bitmap AuthorAvatar => new(AssetLoader.Open(new Uri("avares://NexusMods.App.UI/Assets/DesignTime/avatar.webp")));
public R3.ReactiveCommand<NavigationInformation> OpenCollectionDownloadPageCommand { get; } = new(canExecuteSource: R3.Observable.Return(false), initialCanExecute: false);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
xmlns:collections="clr-namespace:NexusMods.App.UI.Pages.LibraryPage.Collections"
xmlns:panels="clr-namespace:Avalonia.Labs.Panels;assembly=Avalonia.Labs.Panels"
xmlns:icons="clr-namespace:NexusMods.Icons;assembly=NexusMods.Icons"
xmlns:navigation="clr-namespace:NexusMods.App.UI.Controls.Navigation"
mc:Ignorable="d" d:DesignWidth="508" d:DesignHeight="288"
x:Class="NexusMods.App.UI.Pages.LibraryPage.Collections.CollectionCardView">
<Design.DataContext>
Expand Down Expand Up @@ -67,13 +68,13 @@
<panels:FlexPanel x:Name="ActionFooter">

<panels:FlexPanel x:Name="ActionGroup">
<Button x:Name="DownloadButton" Classes="Standard Secondary" Height="28">
<navigation:NavigationControl x:Name="DownloadButton" Classes="Standard Secondary" Height="28">
<panels:FlexPanel>
<icons:UnifiedIcon Value="{x:Static icons:IconValues.Download}" />
<TextBlock Text="Continue Download" />
<icons:UnifiedIcon Value="{x:Static icons:IconValues.ChevronRight}" />
</panels:FlexPanel>
</Button>
</navigation:NavigationControl>
</panels:FlexPanel>

<panels:FlexPanel x:Name="FooterLabels">
Expand Down
Loading

0 comments on commit cce17a3

Please sign in to comment.