From fff8d3836ba81e3312389b254b84d5dcbcff8941 Mon Sep 17 00:00:00 2001 From: erri120 Date: Thu, 12 Dec 2024 14:24:20 +0100 Subject: [PATCH 1/5] Open collection JSON file in text editor --- .../NexusModsLibrary.Collections.cs | 17 +++++++-- .../CollectionDownloadDesignViewModel.cs | 1 + .../CollectionDownloadView.axaml | 7 ++++ .../CollectionDownloadView.axaml.cs | 3 ++ .../CollectionDownloadViewModel.cs | 25 +++++++++++++ .../ICollectionDownloadViewModel.cs | 1 + .../ItemContentsFileTreeViewModel.cs | 4 +- .../Pages/TextEdit/TextEditorPage.cs | 7 +++- .../Pages/TextEdit/TextEditorPageViewModel.cs | 30 +++++++++------ .../CollectionDownloader.cs | 13 +++++++ .../InstallCollectionJob.cs | 37 ------------------- 11 files changed, 88 insertions(+), 57 deletions(-) diff --git a/src/Networking/NexusMods.Networking.NexusWebApi/NexusModsLibrary.Collections.cs b/src/Networking/NexusMods.Networking.NexusWebApi/NexusModsLibrary.Collections.cs index af1c84b7d5..a2bba5aec2 100644 --- a/src/Networking/NexusMods.Networking.NexusWebApi/NexusModsLibrary.Collections.cs +++ b/src/Networking/NexusMods.Networking.NexusWebApi/NexusModsLibrary.Collections.cs @@ -308,10 +308,7 @@ public async ValueTask ParseCollectionJsonFile( NexusModsCollectionLibraryFile.ReadOnly collectionLibraryFile, CancellationToken cancellationToken) { - if (!collectionLibraryFile.AsLibraryFile().TryGetAsLibraryArchive(out var archive)) - throw new InvalidOperationException("The source collection is not a library archive"); - - var jsonFileEntity = archive.Children.FirstOrDefault(f => f.Path == "collection.json"); + var jsonFileEntity = GetCollectionJsonFile(collectionLibraryFile); await using var data = await _fileStore.GetFileStream(jsonFileEntity.AsLibraryFile().Hash, token: cancellationToken); var root = await JsonSerializer.DeserializeAsync(data, _jsonSerializerOptions, cancellationToken: cancellationToken); @@ -319,4 +316,16 @@ public async ValueTask ParseCollectionJsonFile( if (root is null) throw new InvalidOperationException("Unable to deserialize collection JSON file"); return root; } + + /// + /// Gets the collection JSON file. + /// + public LibraryArchiveFileEntry.ReadOnly GetCollectionJsonFile(NexusModsCollectionLibraryFile.ReadOnly collectionLibraryFile) + { + if (!collectionLibraryFile.AsLibraryFile().TryGetAsLibraryArchive(out var archive)) + throw new InvalidOperationException("The source collection is not a library archive"); + + var jsonFileEntity = archive.Children.FirstOrDefault(f => f.Path == "collection.json"); + return jsonFileEntity; + } } diff --git a/src/NexusMods.App.UI/Pages/CollectionDownload/CollectionDownloadDesignViewModel.cs b/src/NexusMods.App.UI/Pages/CollectionDownload/CollectionDownloadDesignViewModel.cs index ba04d07741..0021ea58a2 100644 --- a/src/NexusMods.App.UI/Pages/CollectionDownload/CollectionDownloadDesignViewModel.cs +++ b/src/NexusMods.App.UI/Pages/CollectionDownload/CollectionDownloadDesignViewModel.cs @@ -45,6 +45,7 @@ public CollectionDownloadDesignViewModel() : base(new DesignWindowManager()) { } public ReactiveCommand CommandViewOnNexusMods { get; } = new ReactiveCommand(); public ReactiveCommand CommandViewInLibrary { get; } = new ReactiveCommand(); + public ReactiveCommand CommandOpenJsonFile { get; } = new ReactiveCommand(); public ReactiveCommand CommandDeleteAllDownloads { get; } = new ReactiveCommand(); public ReactiveCommand CommandDeleteCollection { get; } = new ReactiveCommand(); } diff --git a/src/NexusMods.App.UI/Pages/CollectionDownload/CollectionDownloadView.axaml b/src/NexusMods.App.UI/Pages/CollectionDownload/CollectionDownloadView.axaml index 8ff3bb6502..9e1986de94 100644 --- a/src/NexusMods.App.UI/Pages/CollectionDownload/CollectionDownloadView.axaml +++ b/src/NexusMods.App.UI/Pages/CollectionDownload/CollectionDownloadView.axaml @@ -31,6 +31,13 @@ + + + + Open JSON file + + + diff --git a/src/NexusMods.App.UI/Pages/CollectionDownload/CollectionDownloadView.axaml.cs b/src/NexusMods.App.UI/Pages/CollectionDownload/CollectionDownloadView.axaml.cs index 4b89d01fbb..91151e8820 100644 --- a/src/NexusMods.App.UI/Pages/CollectionDownload/CollectionDownloadView.axaml.cs +++ b/src/NexusMods.App.UI/Pages/CollectionDownload/CollectionDownloadView.axaml.cs @@ -27,6 +27,9 @@ public CollectionDownloadView() this.BindCommand(ViewModel, vm => vm.CommandViewInLibrary, view => view.MenuItemViewInLibrary) .DisposeWith(d); + this.BindCommand(ViewModel, vm => vm.CommandOpenJsonFile, view => view.MenuItemOpenJsonFile) + .DisposeWith(d); + this.BindCommand(ViewModel, vm => vm.CommandDeleteAllDownloads, view => view.MenuItemDeleteAllDownloads) .DisposeWith(d); diff --git a/src/NexusMods.App.UI/Pages/CollectionDownload/CollectionDownloadViewModel.cs b/src/NexusMods.App.UI/Pages/CollectionDownload/CollectionDownloadViewModel.cs index f0881ae290..329222e8e4 100644 --- a/src/NexusMods.App.UI/Pages/CollectionDownload/CollectionDownloadViewModel.cs +++ b/src/NexusMods.App.UI/Pages/CollectionDownload/CollectionDownloadViewModel.cs @@ -16,6 +16,7 @@ using NexusMods.App.UI.Extensions; using NexusMods.App.UI.Pages.LibraryPage; using NexusMods.App.UI.Pages.LibraryPage.Collections; +using NexusMods.App.UI.Pages.TextEdit; using NexusMods.App.UI.Resources; using NexusMods.App.UI.Windows; using NexusMods.App.UI.WorkspaceSystem; @@ -61,6 +62,9 @@ public CollectionDownloadViewModel( _revision = revisionMetadata; _collection = revisionMetadata.Collection; + var libraryFile = collectionDownloader.GetLibraryFile(revisionMetadata); + var collectionJsonFile = nexusModsLibrary.GetCollectionJsonFile(libraryFile); + TabTitle = _collection.Name; TabIcon = IconValues.Collections; @@ -99,6 +103,7 @@ public CollectionDownloadViewModel( executeAsync: async (_, _) => { await InstallCollectionJob.Create( serviceProvider, targetLoadout, + source: libraryFile, revisionMetadata, items: collectionDownloader.GetItems(revisionMetadata, CollectionDownloader.ItemType.Required), group: Optional.None @@ -149,6 +154,25 @@ public CollectionDownloadViewModel( CommandViewInLibrary = new ReactiveCommand(canExecuteSource: R3.Observable.Return(false), initialCanExecute: false); + CommandOpenJsonFile = new ReactiveCommand( + execute: _ => + { + var pageData = new PageData + { + FactoryId = TextEditorPageFactory.StaticId, + Context = new TextEditorPageContext + { + FileId = collectionJsonFile.AsLibraryFile().LibraryFileId, + FilePath = collectionJsonFile.AsLibraryFile().FileName, + }, + }; + + var workspaceController = GetWorkspaceController(); + var behavior = new OpenPageBehavior.NewTab(PanelId); + workspaceController.OpenPage(WorkspaceId, pageData, behavior); + } + ); + this.WhenActivated(disposables => { TreeDataGridAdapter.Activate(); @@ -264,6 +288,7 @@ public CollectionDownloadViewModel( public ReactiveCommand CommandViewOnNexusMods { get; } public ReactiveCommand CommandViewInLibrary { get; } + public ReactiveCommand CommandOpenJsonFile { get; } public ReactiveCommand CommandDeleteAllDownloads { get; } public ReactiveCommand CommandDeleteCollection { get; } } diff --git a/src/NexusMods.App.UI/Pages/CollectionDownload/ICollectionDownloadViewModel.cs b/src/NexusMods.App.UI/Pages/CollectionDownload/ICollectionDownloadViewModel.cs index fac56f6991..4c27238aee 100644 --- a/src/NexusMods.App.UI/Pages/CollectionDownload/ICollectionDownloadViewModel.cs +++ b/src/NexusMods.App.UI/Pages/CollectionDownload/ICollectionDownloadViewModel.cs @@ -93,6 +93,7 @@ public interface ICollectionDownloadViewModel : IPageViewModelInterface ReactiveCommand CommandViewOnNexusMods { get; } ReactiveCommand CommandViewInLibrary { get; } + ReactiveCommand CommandOpenJsonFile { get; } ReactiveCommand CommandDeleteAllDownloads { get; } ReactiveCommand CommandDeleteCollection { get; } } diff --git a/src/NexusMods.App.UI/Pages/ItemContentsFileTree/ItemContentsFileTreeViewModel.cs b/src/NexusMods.App.UI/Pages/ItemContentsFileTree/ItemContentsFileTreeViewModel.cs index 891988ece6..b26a767bb1 100644 --- a/src/NexusMods.App.UI/Pages/ItemContentsFileTree/ItemContentsFileTreeViewModel.cs +++ b/src/NexusMods.App.UI/Pages/ItemContentsFileTree/ItemContentsFileTreeViewModel.cs @@ -53,8 +53,8 @@ public ItemContentsFileTreeViewModel( FactoryId = TextEditorPageFactory.StaticId, Context = new TextEditorPageContext { - LoadoutFileId = loadoutFile, - FilePath = loadoutFile.AsLoadoutItemWithTargetPath().TargetPath, + FileId = loadoutFile.LoadoutFileId, + FilePath = loadoutFile.AsLoadoutItemWithTargetPath().TargetPath.Item3, }, }; diff --git a/src/NexusMods.App.UI/Pages/TextEdit/TextEditorPage.cs b/src/NexusMods.App.UI/Pages/TextEdit/TextEditorPage.cs index a6fef6a250..9e2415a817 100644 --- a/src/NexusMods.App.UI/Pages/TextEdit/TextEditorPage.cs +++ b/src/NexusMods.App.UI/Pages/TextEdit/TextEditorPage.cs @@ -1,18 +1,21 @@ using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; using NexusMods.Abstractions.GameLocators; +using NexusMods.Abstractions.Library.Models; using NexusMods.Abstractions.Loadouts; using NexusMods.Abstractions.Loadouts.Files; using NexusMods.Abstractions.Serialization.Attributes; using NexusMods.App.UI.WorkspaceSystem; +using NexusMods.Paths; +using OneOf; namespace NexusMods.App.UI.Pages.TextEdit; [JsonName("TextEditorPageContext")] public record TextEditorPageContext : IPageFactoryContext { - public required LoadoutFileId LoadoutFileId { get; init; } - public required GamePath FilePath { get; init; } + public required OneOf FileId { get; init; } + public required RelativePath FilePath { get; init; } } [UsedImplicitly] diff --git a/src/NexusMods.App.UI/Pages/TextEdit/TextEditorPageViewModel.cs b/src/NexusMods.App.UI/Pages/TextEdit/TextEditorPageViewModel.cs index 4ee0f0edf5..4fefc6cf84 100644 --- a/src/NexusMods.App.UI/Pages/TextEdit/TextEditorPageViewModel.cs +++ b/src/NexusMods.App.UI/Pages/TextEdit/TextEditorPageViewModel.cs @@ -9,6 +9,7 @@ using Microsoft.Extensions.Logging; using NexusMods.Abstractions.IO; using NexusMods.Abstractions.IO.StreamFactories; +using NexusMods.Abstractions.Library.Models; using NexusMods.Abstractions.Loadouts; using NexusMods.Abstractions.Settings; using NexusMods.App.UI.Extensions; @@ -61,11 +62,12 @@ public TextEditorPageViewModel( _loadFileCommand = ReactiveCommand.CreateFromTask>(async context => { - var loadoutFileId = context.LoadoutFileId; - var loadoutFile = LoadoutFile.Load(connection.Db, loadoutFileId); + var hash = context.FileId.Match( + f0: loadoutFileId => LoadoutFile.Load(connection.Db, loadoutFileId).Hash, + f1: libraryFileId => LibraryFile.Load(connection.Db, libraryFileId).Hash + ); - logger.LogDebug("Loading file `{File}` (`{Hash}`) into the Text Editor", loadoutFile.AsLoadoutItemWithTargetPath().TargetPath, loadoutFile.Hash); - await using var stream = await fileStore.GetFileStream(loadoutFile.Hash); + await using var stream = await fileStore.GetFileStream(hash); using var reader = new StreamReader(stream, Encoding.UTF8); var contents = await reader.ReadToEndAsync(); @@ -83,9 +85,7 @@ public TextEditorPageViewModel( { Debug.Assert(Context is not null); - var loadoutFileId = Context!.LoadoutFileId; - var filePath = Context!.FilePath; - + var loadoutFileId = Context.FileId.AsT0; var text = Document.Text; // hash and store the new contents @@ -93,7 +93,7 @@ public TextEditorPageViewModel( var hash = Hash.From(XxHash3.HashToUInt64(bytes)); var size = Size.From((ulong)bytes.Length); - using (var streamFactory = new MemoryStreamFactory(filePath.Path, new MemoryStream(bytes, writable: false))) + using (var streamFactory = new MemoryStreamFactory(Context.FilePath, new MemoryStream(bytes, writable: false))) { if (!await fileStore.HaveFile(hash)) await fileStore.BackupFiles([new ArchivedFileEntry(streamFactory, hash, size)], deduplicate: false); @@ -115,9 +115,15 @@ public TextEditorPageViewModel( { this.WhenAnyValue(vm => vm.Context) .WhereNotNull() - .Select(context => connection.ObserveDatoms(SliceDescriptor.Create(context.LoadoutFileId)) - .Select(_ => context) - ) + .Select(context => + { + var sliceDescriptor = context.FileId.Match( + f0: loadoutFileId => SliceDescriptor.Create(loadoutFileId), + f1: libraryFileId => SliceDescriptor.Create(libraryFileId) + ); + + return connection.ObserveDatoms(sliceDescriptor).Select(_ => context); + }) .Switch() .OffUi() .InvokeReactiveCommand(_loadFileCommand) @@ -138,7 +144,7 @@ public TextEditorPageViewModel( }; Document = document; - IsReadOnly = false; + IsReadOnly = context.FileId.IsT1; }) .DisposeWith(disposables); diff --git a/src/NexusMods.Collections/CollectionDownloader.cs b/src/NexusMods.Collections/CollectionDownloader.cs index adfd32e619..729f831ca2 100644 --- a/src/NexusMods.Collections/CollectionDownloader.cs +++ b/src/NexusMods.Collections/CollectionDownloader.cs @@ -13,6 +13,7 @@ using NexusMods.Abstractions.NexusWebApi; using NexusMods.CrossPlatform.Process; using NexusMods.MnemonicDB.Abstractions; +using NexusMods.MnemonicDB.Abstractions.IndexSegments; using NexusMods.MnemonicDB.Abstractions.Query; using NexusMods.MnemonicDB.Abstractions.TxFunctions; using NexusMods.Networking.NexusWebApi; @@ -376,4 +377,16 @@ public CollectionDownload.ReadOnly[] GetItems(CollectionRevisionMetadata.ReadOnl Array.Resize(ref res, newSize: i); return res; } + + public NexusModsCollectionLibraryFile.ReadOnly GetLibraryFile(CollectionRevisionMetadata.ReadOnly revisionMetadata) + { + var datoms = _connection.Db.Datoms( + (NexusModsCollectionLibraryFile.CollectionSlug, revisionMetadata.Collection.Slug), + (NexusModsCollectionLibraryFile.CollectionRevisionNumber, revisionMetadata.RevisionNumber) + ); + + if (datoms.Count == 0) throw new Exception($"Unable to find collection file for revision `{revisionMetadata.Collection.Slug}` (`{revisionMetadata.RevisionNumber}`)"); + var source = NexusModsCollectionLibraryFile.Load(_connection.Db, datoms[0]); + return source; + } } diff --git a/src/NexusMods.Collections/InstallCollectionJob.cs b/src/NexusMods.Collections/InstallCollectionJob.cs index bb0b192688..01026ed5d8 100644 --- a/src/NexusMods.Collections/InstallCollectionJob.cs +++ b/src/NexusMods.Collections/InstallCollectionJob.cs @@ -65,43 +65,6 @@ public class InstallCollectionJob : IJobDefinitionWithStart(job); } - /// - /// Factory. - /// - public static IJobTask Create( - IServiceProvider provider, - LoadoutId target, - CollectionRevisionMetadata.ReadOnly revisionMetadata, - CollectionDownload.ReadOnly[] items, - Optional group) - { - var connection = provider.GetRequiredService(); - var datoms = connection.Db.Datoms( - (NexusModsCollectionLibraryFile.CollectionSlug, revisionMetadata.Collection.Slug), - (NexusModsCollectionLibraryFile.CollectionRevisionNumber, revisionMetadata.RevisionNumber) - ); - - if (datoms.Count == 0) throw new Exception($"Unable to find collection file for revision `{revisionMetadata.Collection.Slug}` (`{revisionMetadata.RevisionNumber}`)"); - var source = NexusModsCollectionLibraryFile.Load(connection.Db, datoms[0]); - - var monitor = provider.GetRequiredService(); - var job = new InstallCollectionJob - { - Items = items, - Group = group, - TargetLoadout = target, - SourceCollection = source, - RevisionMetadata = revisionMetadata, - ServiceProvider = provider, - FileStore = provider.GetRequiredService(), - Connection = connection, - LibraryService = provider.GetRequiredService(), - NexusModsLibrary = provider.GetRequiredService(), - }; - - return monitor.Begin(job); - } - /// /// Installs the collection. /// From eb84e7960c09bbc74b0b03ace2118a36be15926c Mon Sep 17 00:00:00 2001 From: erri120 Date: Thu, 12 Dec 2024 14:40:42 +0100 Subject: [PATCH 2/5] Rename and document more types --- .../Json/ModSource.cs | 35 ++++++++++++++----- .../Json/ModSourceType.cs | 30 +++++++++++++--- .../NexusModsLibrary.Collections.cs | 8 ++--- 3 files changed, 57 insertions(+), 16 deletions(-) diff --git a/src/Abstractions/NexusMods.Abstractions.Collections/Json/ModSource.cs b/src/Abstractions/NexusMods.Abstractions.Collections/Json/ModSource.cs index ba24719d88..49358828c7 100644 --- a/src/Abstractions/NexusMods.Abstractions.Collections/Json/ModSource.cs +++ b/src/Abstractions/NexusMods.Abstractions.Collections/Json/ModSource.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using JetBrains.Annotations; using NexusMods.Abstractions.Collections.Types; using NexusMods.Abstractions.MnemonicDB.Attributes; using NexusMods.Abstractions.NexusWebApi.Types.V2; @@ -6,23 +7,41 @@ namespace NexusMods.Abstractions.Collections.Json; +/// +/// Polymorphic source +/// +[UsedImplicitly(ImplicitUseTargetFlags.WithMembers)] public class ModSource { + /// + /// Type. + /// [JsonPropertyName("type")] [JsonConverter(typeof(JsonStringEnumConverter))] public required ModSourceType Type { get; init; } - + + /// + /// For : Nexus Mods Mod ID. + /// [JsonPropertyName("modId")] public ModId ModId { get; init; } - + /// - /// MD5 hash a direct download + /// For : Nexus Mods File ID. + /// + [JsonPropertyName("fileId")] + public FileId FileId { get; init; } + + /// + /// MD5 hash, present in , + /// , and . /// [JsonPropertyName("md5")] public Md5HashValue Md5 { get; init; } - + /// - /// If this is a direct download, this is the URL to download the mod from + /// Present in and , + /// url to the download page. /// [JsonPropertyName("url")] public Uri? Url { get; init; } @@ -33,12 +52,12 @@ public class ModSource [JsonPropertyName("logicalFilename")] public string? LogicalFilename { get; init; } - [JsonPropertyName("fileId")] - public FileId FileId { get; init; } - [JsonPropertyName("fileSize")] public Size FileSize { get; init; } [JsonPropertyName("fileExpression")] public RelativePath FileExpression { get; init; } = default; + + [JsonPropertyName("tag")] + public string? Tag { get; init; } } diff --git a/src/Abstractions/NexusMods.Abstractions.Collections/Json/ModSourceType.cs b/src/Abstractions/NexusMods.Abstractions.Collections/Json/ModSourceType.cs index c01cbe6ef7..e0fb7ef82d 100644 --- a/src/Abstractions/NexusMods.Abstractions.Collections/Json/ModSourceType.cs +++ b/src/Abstractions/NexusMods.Abstractions.Collections/Json/ModSourceType.cs @@ -1,4 +1,7 @@ // ReSharper disable InconsistentNaming + +using System.Text.Json.Serialization; + namespace NexusMods.Abstractions.Collections.Json; /// @@ -6,8 +9,27 @@ namespace NexusMods.Abstractions.Collections.Json; /// public enum ModSourceType { - browse, - direct, - nexus, - bundle, + /// + /// Sourced from Nexus Mods. + /// + [JsonStringEnumMemberName("nexus")] + NexusMods, + + /// + /// Bundled with the collection archive. + /// + [JsonStringEnumMemberName("bundle")] + Bundle, + + /// + /// Downloaded externally via an URL. + /// + [JsonStringEnumMemberName("Browse")] + Browse, + + /// + /// Downloaded externally via an URL. + /// + [JsonStringEnumMemberName("Direct")] + Direct, } diff --git a/src/Networking/NexusMods.Networking.NexusWebApi/NexusModsLibrary.Collections.cs b/src/Networking/NexusMods.Networking.NexusWebApi/NexusModsLibrary.Collections.cs index a2bba5aec2..5e62cce500 100644 --- a/src/Networking/NexusMods.Networking.NexusWebApi/NexusModsLibrary.Collections.cs +++ b/src/Networking/NexusMods.Networking.NexusWebApi/NexusModsLibrary.Collections.cs @@ -114,7 +114,7 @@ private static ResolvedEntitiesLookup ResolveModFiles( foreach (var collectionMod in collectionRoot.Mods) { - if (collectionMod.Source.Type != ModSourceType.nexus) continue; + if (collectionMod.Source.Type != ModSourceType.NexusMods) continue; var fileId = new UidForFile(fileId: collectionMod.Source.FileId, gameId: gameIds[collectionMod.DomainName]); if (res.ContainsKey(fileId)) continue; @@ -169,13 +169,13 @@ private static void UpdateFiles( var source = collectionMod.Source; switch (source.Type) { - case ModSourceType.nexus: + case ModSourceType.NexusMods: HandleNexusModsDownload(db, tx, downloadEntity, collectionMod, gameIds, resolvedEntitiesLookup); break; - case ModSourceType.direct or ModSourceType.browse: + case ModSourceType.Direct or ModSourceType.Browse: HandleExternalDownload(tx, downloadEntity, collectionMod); break; - case ModSourceType.bundle: + case ModSourceType.Bundle: HandleBundledFiles(tx, downloadEntity, collectionMod); break; default: From 4024fbb7d672bc9cc1049fc5628d4503097bd3ed Mon Sep 17 00:00:00 2001 From: erri120 Date: Thu, 12 Dec 2024 14:43:23 +0100 Subject: [PATCH 3/5] Add update policy --- .../Json/ModSource.cs | 7 +++++ .../Json/UpdatePolicy.cs | 30 +++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 src/Abstractions/NexusMods.Abstractions.Collections/Json/UpdatePolicy.cs diff --git a/src/Abstractions/NexusMods.Abstractions.Collections/Json/ModSource.cs b/src/Abstractions/NexusMods.Abstractions.Collections/Json/ModSource.cs index 49358828c7..abbc3435b7 100644 --- a/src/Abstractions/NexusMods.Abstractions.Collections/Json/ModSource.cs +++ b/src/Abstractions/NexusMods.Abstractions.Collections/Json/ModSource.cs @@ -20,6 +20,13 @@ public class ModSource [JsonConverter(typeof(JsonStringEnumConverter))] public required ModSourceType Type { get; init; } + /// + /// Update policy. + /// + [JsonPropertyName("updatePolicy")] + [JsonConverter(typeof(JsonStringEnumConverter))] + public UpdatePolicy UpdatePolicy { get; init; } + /// /// For : Nexus Mods Mod ID. /// diff --git a/src/Abstractions/NexusMods.Abstractions.Collections/Json/UpdatePolicy.cs b/src/Abstractions/NexusMods.Abstractions.Collections/Json/UpdatePolicy.cs new file mode 100644 index 0000000000..c8d4943083 --- /dev/null +++ b/src/Abstractions/NexusMods.Abstractions.Collections/Json/UpdatePolicy.cs @@ -0,0 +1,30 @@ +using System.Text.Json.Serialization; +using JetBrains.Annotations; + +namespace NexusMods.Abstractions.Collections.Json; + +/// +/// Update policy. +/// +[PublicAPI] +public enum UpdatePolicy +{ + /// + /// Use the exact version. + /// + [JsonStringEnumMemberName("exact")] + ExactVersionOnly, + + /// + /// Use the current version, if it's still available. + /// If the file has been archived or deleted, the newest version of the file should be used. + /// + [JsonStringEnumMemberName("prefer")] + PreferExact, + + /// + /// Use the latest version. + /// + [JsonStringEnumMemberName("latest")] + LatestVersion, +} From 19dbc9e16cc41b57d57a08fb923edf66a342028b Mon Sep 17 00:00:00 2001 From: erri120 Date: Thu, 12 Dec 2024 14:46:08 +0100 Subject: [PATCH 4/5] Remove bad logging statement --- .../Types/IpcHandlers/NxmIpcProtocolHandler.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/NexusMods.App.Cli/Types/IpcHandlers/NxmIpcProtocolHandler.cs b/src/NexusMods.App.Cli/Types/IpcHandlers/NxmIpcProtocolHandler.cs index 0ed2b9451b..4e0e5128a9 100644 --- a/src/NexusMods.App.Cli/Types/IpcHandlers/NxmIpcProtocolHandler.cs +++ b/src/NexusMods.App.Cli/Types/IpcHandlers/NxmIpcProtocolHandler.cs @@ -147,10 +147,6 @@ private async Task HandleModUrl(CancellationToken cancel, NXMModUrl modUrl) var downloadJob = await nexusModsLibrary.CreateDownloadJob(destination, modUrl, cache, cancellationToken: cancel); var libraryJob = await library.AddDownload(downloadJob); - _logger.LogInformation("{Result}", libraryJob); - - // var task = await _downloadService.AddTask(modUrl); - // _ = task.StartAsync(); } } From a0e69a43b7a1085c4337aa3e4859ec5003025878 Mon Sep 17 00:00:00 2001 From: erri120 Date: Thu, 12 Dec 2024 16:19:43 +0100 Subject: [PATCH 5/5] Fix count Fixes #2383. --- .../CollectionDownloadViewModel.cs | 14 ++------------ src/NexusMods.Collections/CollectionDownloader.cs | 10 ++++++++++ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/NexusMods.App.UI/Pages/CollectionDownload/CollectionDownloadViewModel.cs b/src/NexusMods.App.UI/Pages/CollectionDownload/CollectionDownloadViewModel.cs index 329222e8e4..c8111daa14 100644 --- a/src/NexusMods.App.UI/Pages/CollectionDownload/CollectionDownloadViewModel.cs +++ b/src/NexusMods.App.UI/Pages/CollectionDownload/CollectionDownloadViewModel.cs @@ -71,18 +71,8 @@ public CollectionDownloadViewModel( TreeDataGridAdapter = new CollectionDownloadTreeDataGridAdapter(nexusModsDataProvider, revisionMetadata); TreeDataGridAdapter.ViewHierarchical.Value = false; - var requiredDownloadCount = 0; - var optionalDownloadCount = 0; - foreach (var file in _revision.Downloads) - { - var isOptional = file.IsOptional; - - requiredDownloadCount += isOptional ? 0 : 1; - optionalDownloadCount += isOptional ? 1 : 0; - } - - RequiredDownloadsCount = requiredDownloadCount; - OptionalDownloadsCount = optionalDownloadCount; + RequiredDownloadsCount = collectionDownloader.CountItems(_revision, CollectionDownloader.ItemType.Required); + OptionalDownloadsCount = collectionDownloader.CountItems(_revision, CollectionDownloader.ItemType.Optional); CommandDownloadRequiredItems = _canDownloadRequiredItems.ToReactiveCommand( executeAsync: (_, cancellationToken) => collectionDownloader.DownloadItems(_revision, itemType: CollectionDownloader.ItemType.Required, db: connection.Db, cancellationToken: cancellationToken), diff --git a/src/NexusMods.Collections/CollectionDownloader.cs b/src/NexusMods.Collections/CollectionDownloader.cs index 729f831ca2..6624d6de8e 100644 --- a/src/NexusMods.Collections/CollectionDownloader.cs +++ b/src/NexusMods.Collections/CollectionDownloader.cs @@ -258,6 +258,16 @@ public IObservable DownloadedItemCountObservable(CollectionRevisionMetadata .Prepend(0); } + /// + /// Counts the items. + /// + public int CountItems(CollectionRevisionMetadata.ReadOnly revisionMetadata, ItemType itemType) + { + return revisionMetadata.Downloads + .Where(download => DownloadMatchesItemType(download, itemType)) + .Count(download => download.IsCollectionDownloadNexusMods() || download.IsCollectionDownloadExternal()); + } + /// /// Returns whether the item matches the given item type. ///