From 5d4479f496eb8d7e187e46c4bd6b7603388db329 Mon Sep 17 00:00:00 2001 From: Timothy Baldridge Date: Tue, 1 Oct 2024 12:30:23 -0600 Subject: [PATCH] Non downloaded library items (#2101) * Make Nexus Library Items inherit from LibraryItem * Fix bugs in the initial implementation * Bit 'o cleanup * Fix compile error * Rename NexusModsLibraryFile to NexusMOdsLibraryItem --- .../NexusModsFileMetadata.cs | 6 +++--- .../NexusModsLibraryFile.cs | 8 ++++---- .../Services.cs | 2 +- .../NexusModsDownloadJob.cs | 20 +++++++++---------- .../NexusModsLibrary.cs | 7 ++++++- .../Pages/Library/LibraryItemRemovalInfo.cs | 19 ++++++++---------- .../Pages/NexusModsDataProvider.cs | 19 +++++++++--------- .../InstallCollectionJob.cs | 5 ++++- 8 files changed, 45 insertions(+), 41 deletions(-) diff --git a/src/Abstractions/NexusMods.Abstractions.NexusModsLibrary/NexusModsFileMetadata.cs b/src/Abstractions/NexusMods.Abstractions.NexusModsLibrary/NexusModsFileMetadata.cs index 404c0cb288..9abb540761 100644 --- a/src/Abstractions/NexusMods.Abstractions.NexusModsLibrary/NexusModsFileMetadata.cs +++ b/src/Abstractions/NexusMods.Abstractions.NexusModsLibrary/NexusModsFileMetadata.cs @@ -28,9 +28,9 @@ public partial class NexusModsFileMetadata : IModelDefinition /// The version of the file. /// public static readonly StringAttribute Version = new(Namespace, nameof(Version)); - + /// - /// The size in bytes of the file. + /// The size of the file in bytes, this is optional in the NexusMods API for whatever reason. /// public static readonly SizeAttribute Size = new(Namespace, nameof(Size)) { IsOptional = true }; @@ -42,5 +42,5 @@ public partial class NexusModsFileMetadata : IModelDefinition /// /// Library Files that link to this file. /// - public static readonly BackReferenceAttribute LibraryFiles = new(NexusModsLibraryFile.FileMetadata); + public static readonly BackReferenceAttribute LibraryFiles = new(NexusModsLibraryItem.FileMetadata); } diff --git a/src/Abstractions/NexusMods.Abstractions.NexusModsLibrary/NexusModsLibraryFile.cs b/src/Abstractions/NexusMods.Abstractions.NexusModsLibrary/NexusModsLibraryFile.cs index 325593781a..89855aa0ff 100644 --- a/src/Abstractions/NexusMods.Abstractions.NexusModsLibrary/NexusModsLibraryFile.cs +++ b/src/Abstractions/NexusMods.Abstractions.NexusModsLibrary/NexusModsLibraryFile.cs @@ -6,13 +6,13 @@ namespace NexusMods.Abstractions.NexusModsLibrary; /// -/// Represented a originating from Nexus Mods. +/// Represented a originating from Nexus Mods. /// [PublicAPI] -[Include] -public partial class NexusModsLibraryFile : IModelDefinition +[Include] +public partial class NexusModsLibraryItem : IModelDefinition { - private const string Namespace = "NexusMods.Library.NexusModsLibraryFile"; + private const string Namespace = "NexusMods.Library.NexusModsLibraryItem"; /// /// Remote metadata of the file on Nexus Mods. diff --git a/src/Abstractions/NexusMods.Abstractions.NexusModsLibrary/Services.cs b/src/Abstractions/NexusMods.Abstractions.NexusModsLibrary/Services.cs index 84be9a0a39..ba6052d599 100644 --- a/src/Abstractions/NexusMods.Abstractions.NexusModsLibrary/Services.cs +++ b/src/Abstractions/NexusMods.Abstractions.NexusModsLibrary/Services.cs @@ -18,7 +18,7 @@ public static IServiceCollection AddNexusModsLibraryModels(this IServiceCollecti return serviceCollection .AddNexusModsFileMetadataModel() .AddNexusModsModPageMetadataModel() - .AddNexusModsLibraryFileModel() + .AddNexusModsLibraryItemModel() .AddCollectionMetadataModel() .AddCollectionRevisionMetadataModel() .AddCollectionRevisionModFileModel() diff --git a/src/Networking/NexusMods.Networking.NexusWebApi/NexusModsDownloadJob.cs b/src/Networking/NexusMods.Networking.NexusWebApi/NexusModsDownloadJob.cs index 832b24a1cc..e45e151bf7 100644 --- a/src/Networking/NexusMods.Networking.NexusWebApi/NexusModsDownloadJob.cs +++ b/src/Networking/NexusMods.Networking.NexusWebApi/NexusModsDownloadJob.cs @@ -4,6 +4,7 @@ using NexusMods.Abstractions.Library.Models; using NexusMods.Abstractions.NexusModsLibrary; using NexusMods.MnemonicDB.Abstractions; +using NexusMods.MnemonicDB.Abstractions.TxFunctions; using NexusMods.Networking.HttpDownloader; using NexusMods.Paths; @@ -34,19 +35,18 @@ public async ValueTask StartAsync(IJobContext - public ValueTask AddMetadata(ITransaction transaction, LibraryFile.New libraryFile) + public ValueTask AddMetadata(ITransaction tx, LibraryFile.New libraryFile) { - libraryFile.GetLibraryItem(transaction).Name = FileMetadata.Name; + libraryFile.GetLibraryItem(tx).Name = FileMetadata.Name; - _ = new NexusModsLibraryFile.New(transaction, libraryFile.Id) + // Not using .New here because we can't use the LibraryItem Id and don't have the LibraryItem in this method + tx.Add(libraryFile.Id, NexusModsLibraryItem.FileMetadataId, FileMetadata.Id); + tx.Add(libraryFile.Id, NexusModsLibraryItem.ModPageMetadataId, FileMetadata.ModPage.Id); + + _ = new DownloadedFile.New(tx, libraryFile.Id) { - FileMetadataId = FileMetadata, - ModPageMetadataId = FileMetadata.ModPage, - DownloadedFile = new DownloadedFile.New(transaction, libraryFile.Id) - { - DownloadPageUri = HttpDownloadJob.Job.DownloadPageUri, - LibraryFile = libraryFile, - }, + DownloadPageUri = HttpDownloadJob.Job.DownloadPageUri, + LibraryFile = libraryFile, }; return ValueTask.CompletedTask; diff --git a/src/Networking/NexusMods.Networking.NexusWebApi/NexusModsLibrary.cs b/src/Networking/NexusMods.Networking.NexusWebApi/NexusModsLibrary.cs index e378abe252..93e841f13e 100644 --- a/src/Networking/NexusMods.Networking.NexusWebApi/NexusModsLibrary.cs +++ b/src/Networking/NexusMods.Networking.NexusWebApi/NexusModsLibrary.cs @@ -141,13 +141,18 @@ private async Task DownloadImage(string? uri, CancellationToken token) if (!files.TryGetFirst(x => x.FileId == fileId, out var fileInfo)) throw new NotImplementedException(); - + + var size = Optional.None; + if (fileInfo.SizeInBytes.HasValue) + size = Size.FromLong((long)fileInfo.SizeInBytes!); + var newFile = new NexusModsFileMetadata.New(tx) { Name = fileInfo.Name, Version = fileInfo.Version, FileId = fileId, ModPageId = modPage, + Size = size.HasValue ? size.Value : null, }; if (fileInfo.SizeInBytes.HasValue) diff --git a/src/NexusMods.App.UI/Pages/Library/LibraryItemRemovalInfo.cs b/src/NexusMods.App.UI/Pages/Library/LibraryItemRemovalInfo.cs index 52565842f2..c1343c9394 100644 --- a/src/NexusMods.App.UI/Pages/Library/LibraryItemRemovalInfo.cs +++ b/src/NexusMods.App.UI/Pages/Library/LibraryItemRemovalInfo.cs @@ -20,19 +20,16 @@ public static LibraryItemRemovalInfo Determine(LibraryItem.ReadOnly toRemove, Lo var info = new LibraryItemRemovalInfo(); // Check if it's a file which was downloaded. - var isDownloadedFile = toRemove.TryGetAsDownloadedFile(out var downloadedFile);; - switch (isDownloadedFile) + if (toRemove.TryGetAsNexusModsLibraryItem(out _)) { - case true: - info.IsNexus = downloadedFile.IsNexusModsLibraryFile(); - info.IsNonPermanent = !info.IsNexus; - break; - // Check if it's a LocalFile (manually added) - case false when toRemove.TryGetAsLocalFile(out _): - info.IsManuallyAdded = true; - break; + info.IsNexus = true; + info.IsNonPermanent = !info.IsNexus; } - + else if (toRemove.TryGetAsLocalFile(out _)) + { + info.IsManuallyAdded = true; + } + // Check if it's added to any loadout info.Loadouts = loadouts.Where(loadout => loadout.GetLoadoutItemsByLibraryItem(toRemove).Any()).ToArray(); return info; diff --git a/src/NexusMods.App.UI/Pages/NexusModsDataProvider.cs b/src/NexusMods.App.UI/Pages/NexusModsDataProvider.cs index 90c396a39a..16700f0c1a 100644 --- a/src/NexusMods.App.UI/Pages/NexusModsDataProvider.cs +++ b/src/NexusMods.App.UI/Pages/NexusModsDataProvider.cs @@ -29,7 +29,7 @@ public NexusModsDataProvider(IServiceProvider serviceProvider) public IObservable> ObserveFlatLibraryItems(LibraryFilter libraryFilter) { // NOTE(erri120): For the flat library view, we display each NexusModsLibraryFile - return NexusModsLibraryFile + return NexusModsLibraryItem .ObserveAll(_connection) // only show library files for the currently selected game .FilterOnObservable((file, _) => libraryFilter.GameObservable.Select(game => file.ModPageMetadata.GameDomain.Equals(game.Domain))) @@ -47,13 +47,13 @@ public IObservable> ObserveNestedLibraryI .FilterOnObservable((modPage, _) => libraryFilter.GameObservable.Select(game => modPage.GameDomain.Equals(game.Domain))) // only show mod pages that have library files .FilterOnObservable((_, e) => _connection - .ObserveDatoms(NexusModsLibraryFile.ModPageMetadataId, e) + .ObserveDatoms(NexusModsLibraryItem.ModPageMetadataId, e) .IsNotEmpty() ) .Transform((modPage, _) => ToLibraryItemModel(modPage, libraryFilter)); } - private LibraryItemModel ToLibraryItemModel(NexusModsLibraryFile.ReadOnly nexusModsLibraryFile, LibraryFilter libraryFilter) + private LibraryItemModel ToLibraryItemModel(NexusModsLibraryItem.ReadOnly nexusModsLibraryFile, LibraryFilter libraryFilter) { var linkedLoadoutItemsObservable = QueryHelper.GetLinkedLoadoutItems(_connection, nexusModsLibraryFile.Id, libraryFilter); @@ -64,9 +64,8 @@ private LibraryItemModel ToLibraryItemModel(NexusModsLibraryFile.ReadOnly nexusM }; model.CreatedAtDate.Value = nexusModsLibraryFile.GetCreatedAt(); - model.ItemSize.Value = nexusModsLibraryFile.AsDownloadedFile().AsLibraryFile().Size.ToString(); model.Version.Value = nexusModsLibraryFile.FileMetadata.Version; - + model.ItemSize.Value = nexusModsLibraryFile.FileMetadata.Size.ToString(); return model; } @@ -75,7 +74,7 @@ private LibraryItemModel ToLibraryItemModel(NexusModsModPageMetadata.ReadOnly mo // TODO: dispose var cache = new SourceCache(static datom => datom.E); var disposable = _connection - .ObserveDatoms(NexusModsLibraryFile.ModPageMetadataId, modPageMetadata.Id) + .ObserveDatoms(NexusModsLibraryItem.ModPageMetadataId, modPageMetadata.Id) .AsEntityIds() .Adapt(new SourceCacheAdapter(cache)) .SubscribeWithErrorLogging(); @@ -83,7 +82,7 @@ private LibraryItemModel ToLibraryItemModel(NexusModsModPageMetadata.ReadOnly mo var hasChildrenObservable = cache.Connect().IsNotEmpty(); var childrenObservable = cache.Connect().Transform((_, e) => { - var libraryFile = NexusModsLibraryFile.Load(_connection.Db, e); + var libraryFile = NexusModsLibraryItem.Load(_connection.Db, e); return ToLibraryItemModel(libraryFile, libraryFilter); }); @@ -94,7 +93,7 @@ private LibraryItemModel ToLibraryItemModel(NexusModsModPageMetadata.ReadOnly mo .Transform((_, e) => LibraryLinkedLoadoutItem.Load(_connection.Db, e)); var libraryFilesObservable = cache.Connect() - .Transform((_, e) => NexusModsLibraryFile.Load(_connection.Db, e).AsDownloadedFile().AsLibraryFile().AsLibraryItem()); + .Transform((_, e) => NexusModsLibraryItem.Load(_connection.Db, e).AsLibraryItem()); var numInstalledObservable = cache.Connect().TransformOnObservable((_, e) => _connection .ObserveDatoms(LibraryLinkedLoadoutItem.LibraryItemId, e) @@ -122,7 +121,7 @@ public IObservable> ObserveNestedLoadoutI return NexusModsModPageMetadata .ObserveAll(_connection) .FilterOnObservable((_, modPageEntityId) => _connection - .ObserveDatoms(NexusModsLibraryFile.ModPageMetadataId, modPageEntityId) + .ObserveDatoms(NexusModsLibraryItem.ModPageMetadataId, modPageEntityId) .FilterOnObservable((d, _) => _connection .ObserveDatoms(LibraryLinkedLoadoutItem.LibraryItemId, d.E) .AsEntityIds() @@ -135,7 +134,7 @@ public IObservable> ObserveNestedLoadoutI // TODO: dispose var cache = new SourceCache(static datom => datom.E); var disposable = _connection - .ObserveDatoms(NexusModsLibraryFile.ModPageMetadataId, modPage.Id).AsEntityIds() + .ObserveDatoms(NexusModsLibraryItem.ModPageMetadataId, modPage.Id).AsEntityIds() .FilterOnObservable((_, e) => _connection.ObserveDatoms(LibraryLinkedLoadoutItem.LibraryItemId, e).IsNotEmpty()) // NOTE(erri120): DynamicData 9.0.4 is broken for value types because it uses ReferenceEquals. Temporary workaround is a custom equality comparer. .MergeManyChangeSets((_, e) => _connection.ObserveDatoms(LibraryLinkedLoadoutItem.LibraryItemId, e).AsEntityIds(), equalityComparer: DatomEntityIdEqualityComparer.Instance) diff --git a/src/NexusMods.Collections/InstallCollectionJob.cs b/src/NexusMods.Collections/InstallCollectionJob.cs index bd16ac86c8..ca7b3ccc3c 100644 --- a/src/NexusMods.Collections/InstallCollectionJob.cs +++ b/src/NexusMods.Collections/InstallCollectionJob.cs @@ -211,7 +211,10 @@ private async Task EnsureNexusModDownloaded(Mod mod) .FirstOrOptional(f => f.LibraryFiles.Any()); if (file.HasValue) - return (mod, file.Value.LibraryFiles.First().AsDownloadedFile().AsLibraryFile()); + { + if (!file.Value.LibraryFiles.First().AsLibraryItem().TryGetAsLibraryFile(out var firstLibraryFile)) + return (mod, firstLibraryFile); + } await using var tempPath = TemporaryFileManager.CreateFile();