Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework installing a collection #2328

Merged
merged 22 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,9 @@ public partial class CollectionDownload : IModelDefinition
/// Whether the download is optional.
/// </summary>
public static readonly BooleanAttribute IsOptional = new(Namespace, nameof(IsOptional));

/// <summary>
/// Index into the source array.
/// </summary>
public static readonly Int32Attribute ArrayIndex = new(Namespace, nameof(ArrayIndex)) { IsIndexed = true };
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
using JetBrains.Annotations;
using NexusMods.Abstractions.MnemonicDB.Attributes;
using NexusMods.MnemonicDB.Abstractions.Attributes;
using NexusMods.MnemonicDB.Abstractions.Models;

namespace NexusMods.Abstractions.NexusModsLibrary.Models;

[PublicAPI]
[Include<CollectionDownload>]
public partial class ColletionDownloadBundled : IModelDefinition
public partial class CollectionDownloadBundled : IModelDefinition
{
private const string Namespace = "NexusMods.NexusModsLibrary.CollectionDownloadBundled";

/// <summary>
/// Bundled path.
/// </summary>
public static readonly RelativePathAttribute BundledPath = new(Namespace, nameof(BundledPath));
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using NexusMods.Abstractions.NexusModsLibrary.Attributes;
using NexusMods.Abstractions.NexusWebApi.Types;
using NexusMods.Abstractions.NexusWebApi.Types.V2;
using NexusMods.Abstractions.Resources.DB;
using NexusMods.Abstractions.Telemetry;
using NexusMods.MnemonicDB.Abstractions.Attributes;
using NexusMods.MnemonicDB.Abstractions.Models;

Expand All @@ -22,6 +25,11 @@ public partial class CollectionMetadata : IModelDefinition
/// </summary>
public static readonly StringAttribute Name = new(Namespace, nameof(Name));

/// <summary>
/// The id of the game.
/// </summary>
public static readonly GameIdAttribute GameId = new(Namespace, nameof(GameId)) { IsIndexed = true };

/// <summary>
/// The short description of the collection
/// </summary>
Expand Down Expand Up @@ -71,4 +79,9 @@ public partial class CollectionMetadata : IModelDefinition
/// The background image resource.
/// </summary>
public static readonly ReferenceAttribute<PersistedDbResource> BackgroundImageResource = new(Namespace, nameof(BackgroundImageResource)) { IsOptional = true };

public partial struct ReadOnly
{
public Uri GetUri(GameDomain gameDomain) => NexusModsUrlBuilder.CreateCollectionsUri(gameDomain, Slug);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public Uri GetUri()
// NOTE(erri120): This URI shows a single download button for the exact file
// The nmm=1 turns the button into a nxm:// link, without nmm=1 the button will download the file through the browser
// Example: https://www.nexusmods.com/stardewvalley/mods/29140?tab=files&file_id=115276&nmm=1
return NexusModsUrlBuilder.CreateCollectionsUri($"{ModPage.GetBaseUrl()}?tab=files&file_id={Uid.FileId}&nmm=1");
return NexusModsUrlBuilder.CreateGenericUri($"{ModPage.GetBaseUrl()}?tab=files&file_id={Uid.FileId}&nmm=1");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public static IServiceCollection AddNexusModsLibraryModels(this IServiceCollecti
.AddCollectionDownloadModel()
.AddCollectionDownloadExternalModel()
.AddCollectionDownloadNexusModsModel()
.AddColletionDownloadBundledModel()
.AddCollectionDownloadBundledModel()
.AddCollectionCategoryModel()
.AddUserModel()
.AddNexusModsCollectionLibraryFileModel();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,9 @@ public interface IGameDomainToGameIdMappingCache
/// cache hit unless the translation is being done for the first time ever.
/// </remarks>
Optional<GameDomain> TryGetDomain(GameId gameId, CancellationToken cancellationToken);

/// <summary>
/// Puts the values into the cache.
/// </summary>
ValueTask InsertAsync(GameDomain gameDomain, GameId gameId);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using JetBrains.Annotations;
using Microsoft.AspNetCore.WebUtilities;
using NexusMods.Abstractions.NexusWebApi.Types;

namespace NexusMods.Abstractions.Telemetry;

Expand Down Expand Up @@ -46,7 +47,7 @@ public static Uri CreateUri(string baseUrl, string? campaign = null, string? med
/// </remarks>
public static Uri CreateGenericUri(string baseUrl) => CreateUri(baseUrl);

public static Uri CreateCollectionsUri(string baseUrl) => CreateUri(baseUrl, campaign: "collections");
public static Uri CreateCollectionsUri(GameDomain gameDomain, CollectionSlug collectionSlug) => CreateUri($"https://next.nexusmods.com/{gameDomain}/collections/{collectionSlug}", campaign: "collections");

/// <summary>
/// Creates a new URI pointing to a mod on Nexus Mods. This should only be used
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ query CollectionRevisionInfo($slug: String!, $revisionNumber: Int!, $viewAdultCo
user {
...UserFragment
}
game {
id
name
domainName
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ public LoginManager(
var userInfo = await _msgFactory.Verify(_nexusApiClient, cancellationToken);
_cachedUserInfo.Store(userInfo);

IsPremium = userInfo?.IsPremium ?? false;
return userInfo;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using NexusMods.Abstractions.NexusWebApi.Types.V2.Uid;
using NexusMods.Extensions.BCL;
using NexusMods.MnemonicDB.Abstractions;
using NexusMods.MnemonicDB.Abstractions.TxFunctions;
using NexusMods.Networking.NexusWebApi.Extensions;
using NexusMods.Paths;

Expand Down Expand Up @@ -59,6 +60,33 @@ public partial class NexusModsLibrary
return CollectionRevisionMetadata.Load(results.Db, results[collectionRevisionEntityId]);
}

/// <summary>
/// Deletes a collection, all revisions, and all download entities of all revisions.
/// </summary>
public async ValueTask DeleteCollection(
CollectionMetadataId collectionMetadataId,
CancellationToken cancellationToken)
{
var db = _connection.Db;
using var tx = _connection.BeginTransaction();

var revisionIds = db.Datoms(CollectionRevisionMetadata.CollectionId, collectionMetadataId);
foreach (var revisionId in revisionIds)
{
var downloadIds = db.Datoms(CollectionDownload.CollectionRevision, revisionId.E);
foreach (var downloadId in downloadIds)
{
tx.Delete(downloadId.E, recursive: false);
}

tx.Delete(revisionId.E, recursive: false);
}

tx.Delete(collectionMetadataId, recursive: false);

await tx.Commit();
}

private static ResolvedEntitiesLookup ResolveModFiles(
IDb db,
ITransaction tx,
Expand Down Expand Up @@ -126,10 +154,13 @@ private static void UpdateFiles(
GameIdCache gameIds,
ResolvedEntitiesLookup resolvedEntitiesLookup)
{
foreach (var collectionMod in collectionRoot.Mods)
for (var i = 0; i < collectionRoot.Mods.Length; i++)
{
var collectionMod = collectionRoot.Mods[i];

var downloadEntity = new CollectionDownload.New(tx)
{
ArrayIndex = i,
CollectionRevisionId = collectionRevisionEntityId,
IsOptional = collectionMod.Optional,
Name = collectionMod.Name,
Expand Down Expand Up @@ -169,7 +200,7 @@ private static void HandleNexusModsDownload(

var fileId = new UidForFile(fileId: collectionMod.Source.FileId, gameId: gameIds[collectionMod.DomainName]);

Debug.Assert(resolvedEntitiesLookup.ContainsKey(fileId), message: "Should've resolved all mod files ealier");
Debug.Assert(resolvedEntitiesLookup.ContainsKey(fileId), message: "Should've resolved all mod files earlier");
var (_, fileMetadataId) = resolvedEntitiesLookup[fileId];

_ = new CollectionDownloadNexusMods.New(tx, downloadEntity.Id)
Expand Down Expand Up @@ -206,7 +237,7 @@ private static void HandleBundledFiles(
{
var source = collectionMod.Source;

_ = new ColletionDownloadBundled.New(tx, downloadEntity.Id)
_ = new CollectionDownloadBundled.New(tx, downloadEntity.Id)
{
CollectionDownload = downloadEntity,
BundledPath = source.FileExpression,
Expand Down Expand Up @@ -250,9 +281,9 @@ private static EntityId UpdateCollectionInfo(
var resolver = GraphQLResolver.Create(db, tx, CollectionMetadata.Slug, slug);

resolver.Add(CollectionMetadata.Name, collectionInfo.Name);
resolver.Add(CollectionMetadata.GameId, GameId.From((uint)collectionInfo.Game.Id));
resolver.Add(CollectionMetadata.Summary, collectionInfo.Summary);
resolver.Add(CollectionMetadata.Endorsements, (ulong)collectionInfo.Endorsements);

resolver.Add(CollectionMetadata.TotalDownloads, (ulong)collectionInfo.TotalDownloads);

if (Uri.TryCreate(collectionInfo.TileImage?.ThumbnailUrl, UriKind.Absolute, out var tileImageUri))
Expand All @@ -270,7 +301,10 @@ private static EntityId UpdateCollectionInfo(
return resolver.Id;
}

private async ValueTask<CollectionRoot> ParseCollectionJsonFile(
/// <summary>
/// Parses the collection json file.
/// </summary>
public async ValueTask<CollectionRoot> ParseCollectionJsonFile(
NexusModsCollectionLibraryFile.ReadOnly collectionLibraryFile,
CancellationToken cancellationToken)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,8 @@ private async Task<GameDomain> QueryDomainFromIdAsync(GameId gameId, Cancellatio
}
}

private async ValueTask InsertAsync(GameDomain gameDomain, GameId gameId)
/// <inheritdoc/>
public async ValueTask InsertAsync(GameDomain gameDomain, GameId gameId)
{
// Note(sewer): In theory, there's a race condition in here if multiple threads
// try to insert at once. However that should not be a concern here,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,7 @@ public CollectionDownloadDesignViewModel() : base(new DesignWindowManager()) { }

public ReactiveCommand<Unit> DownloadAllCommand { get; } = new ReactiveCommand();
public ReactiveCommand<Unit> InstallCollectionCommand { get; } = new ReactiveCommand();
public ReactiveCommand<Unit> CommandDeleteCollection { get; } = new ReactiveCommand();
public ReactiveCommand<Unit> CommandDeleteAllDownloads { get; } = new ReactiveCommand();
public ReactiveCommand<Unit> CommandViewOnNexusMods { get; } = new ReactiveCommand();
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using NexusMods.Abstractions.Library;
using NexusMods.Abstractions.Loadouts;
using NexusMods.Abstractions.NexusModsLibrary.Models;
using NexusMods.Abstractions.Serialization.Attributes;
using NexusMods.App.UI.WorkspaceSystem;
using NexusMods.MnemonicDB.Abstractions;
using NexusMods.Networking.NexusWebApi;

namespace NexusMods.App.UI.Pages.CollectionDownload;

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

Expand All @@ -35,8 +35,9 @@ public override ICollectionDownloadViewModel CreateViewModel(CollectionDownloadP

return new CollectionDownloadViewModel(
windowManager: WindowManager,
ServiceProvider,
revisionMetadata: metadata
serviceProvider: ServiceProvider,
revisionMetadata: metadata,
targetLoadout: context.TargetLoadout
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,28 @@

<reactiveUi:ReactiveUserControl.Resources>
<MenuFlyout x:Key="CollectionMenuFlyout">
<MenuItem>
<MenuItem x:Name="MenuItemViewOnNexusMods">
<MenuItem.Header>
<panels:FlexPanel>
<TextBlock>View on Nexus Mods</TextBlock>
</panels:FlexPanel>
</MenuItem.Header>
</MenuItem>
<MenuItem>
<MenuItem x:Name="MenuItemViewInLibrary">
<MenuItem.Header>
<panels:FlexPanel>
<TextBlock>View in Library</TextBlock>
</panels:FlexPanel>
</MenuItem.Header>
</MenuItem>
<MenuItem>
<MenuItem x:Name="MenuItemDeleteAllDownloads">
<MenuItem.Header>
<panels:FlexPanel>
<TextBlock>Delete all downloads</TextBlock>
</panels:FlexPanel>
</MenuItem.Header>
</MenuItem>
<MenuItem>
<MenuItem x:Name="MenuItemDeleteCollection">
<MenuItem.Header>
<panels:FlexPanel>
<TextBlock>Delete Collection</TextBlock>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,24 @@ public CollectionDownloadView()

this.WhenActivated(d =>
{
this.BindCommand(ViewModel, vm => vm.InstallCollectionCommand, view => view.InstallButton)
.DisposeWith(d);

this.BindCommand(ViewModel, vm => vm.DownloadAllCommand, view => view.DownloadAllButton)
.DisposeWith(d);

this.BindCommand(ViewModel, vm => vm.CommandDeleteCollection, view => view.MenuItemDeleteCollection)
.DisposeWith(d);

this.BindCommand(ViewModel, vm => vm.CommandDeleteAllDownloads, view => view.MenuItemDeleteAllDownloads)
.DisposeWith(d);

this.BindCommand(ViewModel, vm => vm.CommandViewOnNexusMods, view => view.MenuItemViewOnNexusMods)
.DisposeWith(d);

// TODO:
MenuItemViewInLibrary.IsEnabled = false;

this.OneWayBind(ViewModel, vm => vm.TreeDataGridAdapter.Source.Value, view => view.RequiredDownloadsTree.Source)
.DisposeWith(d);

Expand Down
Loading
Loading