Skip to content

Commit

Permalink
Merge pull request #1829 from erri120/fix/1763-update-filters
Browse files Browse the repository at this point in the history
Add user filters
  • Loading branch information
erri120 authored Jul 31, 2024
2 parents c859beb + 8dd5822 commit 8847c94
Show file tree
Hide file tree
Showing 17 changed files with 125 additions and 114 deletions.
6 changes: 3 additions & 3 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
<PackageVersion Include="LinqGen" Version="0.3.1" />
<PackageVersion Include="Nerdbank.FullDuplexStream" Version="1.1.12" />
<PackageVersion Include="Nerdbank.Streams" Version="2.11.74" />
<PackageVersion Include="NexusMods.MnemonicDB" Version="0.9.67" />
<PackageVersion Include="NexusMods.MnemonicDB.Abstractions" Version="0.9.67" />
<PackageVersion Include="NexusMods.MnemonicDB" Version="0.9.68" />
<PackageVersion Include="NexusMods.MnemonicDB.Abstractions" Version="0.9.68" />
<PackageVersion Include="NexusMods.Paths" Version="0.9.5" />
<PackageVersion Include="NexusMods.Hashing.xxHash64" Version="2.0.1" />
<PackageVersion Include="NexusMods.Paths.TestingHelpers" Version="0.9.5" />
Expand Down Expand Up @@ -115,7 +115,7 @@
<PackageVersion Include="Humanizer" Version="2.14.1" />
<PackageVersion Include="ini-parser-netstandard" Version="2.5.2" />
<PackageVersion Include="Mutagen.Bethesda.Skyrim" Version="0.44.0" />
<PackageVersion Include="NexusMods.MnemonicDB.SourceGenerator" Version="0.9.67" />
<PackageVersion Include="NexusMods.MnemonicDB.SourceGenerator" Version="0.9.68" />
<PackageVersion Include="NLog.Extensions.Logging" Version="5.3.11" />
<PackageVersion Include="OneOf" Version="3.0.271" />
<PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
using JetBrains.Annotations;
using NexusMods.Abstractions.Downloads;
using NexusMods.Abstractions.Jobs;
using NexusMods.Abstractions.Library.Installers;
using NexusMods.Abstractions.Library.Models;
using NexusMods.Abstractions.Loadouts;
using NexusMods.Paths;
using LibraryItem = NexusMods.Abstractions.Library.Models.LibraryItem;

namespace NexusMods.Abstractions.Library;

Expand All @@ -28,6 +29,6 @@ public interface ILibraryService
/// </summary>
/// <param name="libraryItem">The item to install.</param>
/// <param name="targetLoadout">The target loadout.</param>
/// <param name="installer">If set, must be set to a ILibraryItemInstaller (untyped here as to not require a library reference). The Library will use this installer to install the item</param>
IJob InstallItem(LibraryItem.ReadOnly libraryItem, Loadout.ReadOnly targetLoadout, object? installer = null);
/// <param name="installer">The Library will use this installer to install the item</param>
IJob InstallItem(LibraryItem.ReadOnly libraryItem, Loadout.ReadOnly targetLoadout, ILibraryItemInstaller? installer = null);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

<ItemGroup>
<ProjectReference Include="..\NexusMods.Abstractions.Downloads\NexusMods.Abstractions.Downloads.csproj" />
<ProjectReference Include="..\NexusMods.Abstractions.Library.Installers\NexusMods.Abstractions.Library.Installers.csproj" />
<ProjectReference Include="..\NexusMods.Abstractions.Library.Models\NexusMods.Abstractions.Library.Models.csproj" />
<ProjectReference Include="..\NexusMods.Abstractions.Loadouts\NexusMods.Abstractions.Loadouts.csproj" />
</ItemGroup>
Expand Down
14 changes: 13 additions & 1 deletion src/Abstractions/NexusMods.Abstractions.Loadouts/Loadout.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using DynamicData.Cache.Internal;
using Microsoft.Extensions.DependencyInjection;
using NexusMods.Abstractions.GameLocators;
using NexusMods.Abstractions.Library.Models;
using NexusMods.Abstractions.Loadouts.Ids;
using NexusMods.Abstractions.Loadouts.Mods;
using NexusMods.Abstractions.MnemonicDB.Attributes;
Expand Down Expand Up @@ -124,7 +125,18 @@ public LoadoutWithTxId GetLoadoutWithTxId()
/// especially, when it comes to displaying elements the user can edit.
/// </remarks>
public bool IsVisible() => LoadoutKind == LoadoutKind.Default;


/// <summary>
/// Returns an enumerable containing all loadout items linked to the given library item.
/// </summary>
public IEnumerable<LibraryLinkedLoadoutItem.ReadOnly> GetLoadoutItemsByLibraryItem(LibraryItem.ReadOnly libraryItem)
{
return Items.Where(item =>
{
if (!item.TryGetAsLibraryLinkedLoadoutItem(out var linked)) return false;
return linked.LibraryItemId == libraryItem.LibraryItemId;
}).Select(item => item.ToLibraryLinkedLoadoutItem());
}
}
}

Expand Down
35 changes: 35 additions & 0 deletions src/NexusMods.App.UI/Filters/LibraryUserFilters.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using DynamicData;
using DynamicData.Alias;
using JetBrains.Annotations;
using NexusMods.Abstractions.Library.Models;
using NexusMods.MnemonicDB.Abstractions;

namespace NexusMods.App.UI;

/// <summary>
/// Filters for displaying the contents of the Library to the user.
/// </summary>
[PublicAPI]
public static class LibraryUserFilters
{
/// <summary>
/// Returns whether the given library item should be shown to the user.
/// </summary>
public static bool ShouldShow(LibraryItem.ReadOnly libraryItem)
{
if (!libraryItem.TryGetAsLibraryFile(out var file))
return false;

// TODO: also show downloads
return file.IsLocalFile();
}

/// <summary>
/// Returns an observable stream of all library items that should be shown
/// to the user.
/// </summary>
public static IObservable<IChangeSet<LibraryItem.ReadOnly>> ObserveFilteredLibraryItems(IConnection connection)
{
return LibraryItem.ObserveAll(connection).Where(ShouldShow);
}
}
22 changes: 22 additions & 0 deletions src/NexusMods.App.UI/Filters/LoadoutUserFilters.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using JetBrains.Annotations;
using NexusMods.Abstractions.Loadouts;

namespace NexusMods.App.UI;

/// <summary>
/// Filters for displaying the contents of a Loadout to the user.
/// </summary>
[PublicAPI]
public static class LoadoutUserFilters
{
/// <summary>
/// Returns whether the given loadout item should be shown to the user.
/// </summary>
public static bool ShouldShow(LoadoutItem.ReadOnly loadoutItem)
{
if (!loadoutItem.IsLoadoutItemGroup()) return false;

if (!loadoutItem.TryGetAsLibraryLinkedLoadoutItem(out var linked)) return false;
return LibraryUserFilters.ShouldShow(linked.LibraryItem);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.Extensions.DependencyInjection;
using NexusMods.Abstractions.Diagnostics;
using NexusMods.Abstractions.FileStore.Downloads;
using NexusMods.Abstractions.Library;
using NexusMods.Abstractions.Library.Models;
using NexusMods.Abstractions.MnemonicDB.Attributes;
using NexusMods.App.UI.Controls.Navigation;
Expand Down Expand Up @@ -135,12 +136,11 @@ public LoadoutLeftMenuViewModel(
})
.BindToVM(diagnosticItem, vm => vm.Badges)
.DisposeWith(disposable);
LibraryArchive.ObserveAll(conn)
LibraryUserFilters.ObserveFilteredLibraryItems(connection: conn)
.OnUI()
.WhereReasonsAre(ListChangeReason.Add, ListChangeReason.AddRange)
.Filter(model => FileOriginsPageViewModel.FilterDownloadAnalysisModel(model, game.Domain))
.Subscribe(changeSet => NewDownloadModelCount += changeSet.Adds)
.SubscribeWithErrorLogging(changeSet => NewDownloadModelCount += changeSet.Adds)
.DisposeWith(disposable);
// NOTE(erri120): No new downloads when the Left Menu gets loaded. Must be set here because the observable stream
Expand Down
1 change: 1 addition & 0 deletions src/NexusMods.App.UI/NexusMods.App.UI.csproj.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=controls_005Csettings_005Csettingentries_005Csettinginteractioncontrols/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=controls_005Csettings_005Csettingentries_005Csettinginteractioncontrols_005Ccombobox/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=controls_005Csettings_005Csettingentries_005Csettinginteractioncontrols_005Ctoggle/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=filters/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=overlays_005Clogin_005Cstartupmessagebox/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=pages_005Cdiagnostics_005Cdetails/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=pages_005Cdiagnostics_005Clist/@EntryIndexedValue">True</s:Boolean>
Expand Down
22 changes: 1 addition & 21 deletions src/NexusMods.App.UI/Pages/LoadoutGrid/LoadoutGridViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,6 @@ public LoadoutGridViewModel(
var behavior = workspaceController.GetOpenPageBehavior(pageData, info);
workspaceController.OpenPage(WorkspaceId, pageData, behavior);
});

var settings = settingsManager.GetChanges<LoadoutGridSettings>()
.StartWith(settingsManager.Get<LoadoutGridSettings>());

var hasSelection = SelectedGroupIds.CountChanged.Select(count => count > 0);

Expand Down Expand Up @@ -135,13 +132,12 @@ public LoadoutGridViewModel(
.Switch()
.Select(loadout => loadout
.Items
.Where(LoadoutUserFilters.ShouldShow)
.OfTypeLoadoutItemGroup()
.Where(group => !group.Contains(LoadoutItem.ParentId))
.Select(group => group.LoadoutItemGroupId)
)
.OnUI()
.ToDiffedChangeSet(group => group, group => group)
.Filter(settings.Select(ShouldShow))
.Bind(out _groupIds)
.SubscribeWithErrorLogging(logger)
.DisposeWith(d);
Expand All @@ -154,22 +150,6 @@ public LoadoutGridViewModel(
});
}

private Func<LoadoutItemGroupId, bool> ShouldShow(LoadoutGridSettings settings)
{
return itm =>
{
var group = LoadoutItemGroup.Load(_connection.Db, itm);
if (group.Contains(LoadoutGameFilesGroup.GameMetadata) && !settings.ShowGameFiles)
return false;
if (group.Contains(LoadoutOverridesGroup.OverridesForId) && !settings.ShowOverride)
return false;
return true;
};
}

// [UsedImplicitly]
// public async Task DeleteMods(IEnumerable<ModId> modsToDelete, string commitMessage)
// {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
using System.Reactive;
using System.Reactive.Linq;
using DynamicData.Alias;
using Humanizer;
using NexusMods.Abstractions.FileStore.Downloads;
using NexusMods.Abstractions.Library.Models;
using NexusMods.Abstractions.Loadouts;
using NexusMods.Abstractions.Loadouts.Ids;
using NexusMods.Abstractions.Loadouts.Mods;
using NexusMods.Abstractions.MnemonicDB.Attributes.Extensions;
using NexusMods.App.UI.Controls.Navigation;
using NexusMods.MnemonicDB.Abstractions;
using NexusMods.MnemonicDB.Abstractions.Models;
using NexusMods.Networking.Downloaders.Tasks.State;
using NexusMods.Paths;
using ReactiveUI;

Expand All @@ -35,55 +30,54 @@ public class FileOriginEntryViewModel : AViewModel<IFileOriginEntryViewModel>, I

private readonly ObservableAsPropertyHelper<string> _displayLastInstalledDate;
public string DisplayLastInstalledDate => _displayLastInstalledDate.Value;
public LibraryArchive.ReadOnly FileOrigin { get; }
public LibraryFile.ReadOnly LibraryFile { get; }
public ReactiveCommand<NavigationInformation, Unit> ViewModCommand { get; }
public ReactiveCommand<Unit, Unit> AddToLoadoutCommand { get; }
public ReactiveCommand<Unit, Unit> AddAdvancedToLoadoutCommand { get; }

public FileOriginEntryViewModel(
IConnection conn,
LoadoutId loadoutId,
LibraryArchive.ReadOnly fileOrigin,
LibraryFile.ReadOnly libraryFile,
ReactiveCommand<NavigationInformation, Unit> viewModCommand,
ReactiveCommand<Unit, Unit> addModToLoadoutCommand,
ReactiveCommand<Unit, Unit> addAdvancedToLoadoutCommand)
{
FileOrigin = fileOrigin;
LibraryFile = libraryFile;

ViewModCommand = viewModCommand;
AddToLoadoutCommand = addModToLoadoutCommand;
AddAdvancedToLoadoutCommand = addAdvancedToLoadoutCommand;

Name = fileOrigin.AsLibraryFile().AsLibraryItem().Name;
Name = libraryFile.AsLibraryItem().Name;

Size = fileOrigin.AsLibraryFile().Size;
Size = libraryFile.Size;

Version = DownloaderState.Version.TryGet(fileOrigin, out var version) && version != "Unknown" ? version : "-";
ArchiveDate = fileOrigin.GetCreatedAt();
Version = "-";

ArchiveDate = libraryFile.GetCreatedAt();

var loadout = Loadout.Load(conn.Db, loadoutId);

_isModAddedToLoadout = loadout.Revisions()
.Select(rev => rev.Mods.Any(mod => mod.Contains(Mod.Source) && mod.SourceId.Equals(fileOrigin.Id)))
.Select(x => x.GetLoadoutItemsByLibraryItem(libraryFile.AsLibraryItem()).Any())
.ToProperty(this, vm => vm.IsModAddedToLoadout, scheduler: RxApp.MainThreadScheduler);

_lastInstalledDate = loadout.Revisions()
.Select(x => x
.GetLoadoutItemsByLibraryItem(libraryFile.AsLibraryItem())
.Select(item => item.GetCreatedAt())
.DefaultIfEmpty(DateTime.MinValue)
.Max())
.ToProperty(this, vm => vm.LastInstalledDate, scheduler: RxApp.MainThreadScheduler);

var interval = Observable.Interval(TimeSpan.FromSeconds(60)).StartWith(1);

// Update the humanized Archive Date every minute
_displayArchiveDate = interval.Select(_ => ArchiveDate)
.Select(date => date.Equals(DateTime.MinValue) ? "-" : date.Humanize())
.ToProperty(this, vm => vm.DisplayArchiveDate, scheduler: RxApp.MainThreadScheduler);

// Update the LastInstalledDate every time the loadout is updated
_lastInstalledDate = loadout.Revisions()
.Select(rev => rev.Mods.Where(mod => mod.Contains(Mod.Source)
&& mod.SourceId.Equals(fileOrigin.Id))
.Select(mod => mod.GetCreatedAt())
.DefaultIfEmpty(DateTime.MinValue)
.Max()
)
.ToProperty(this, vm => vm.LastInstalledDate, scheduler: RxApp.MainThreadScheduler);

// Update the humanized LastInstalledDate every minute and when the LastInstalledDate changes
_displayLastInstalledDate = this.WhenAnyValue(vm => vm.LastInstalledDate)
.Merge(interval.Select(_ => LastInstalledDate))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public interface IFileOriginEntryViewModel : IViewModelInterface
string DisplayArchiveDate { get; }
string DisplayLastInstalledDate { get; }
bool IsModAddedToLoadout { get; }
LibraryArchive.ReadOnly FileOrigin { get; }
LibraryFile.ReadOnly LibraryFile { get; }
ReactiveCommand<Unit, Unit> AddToLoadoutCommand { get; }
ReactiveCommand<Unit, Unit> AddAdvancedToLoadoutCommand { get; }
ReactiveCommand<NavigationInformation, Unit> ViewModCommand { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@
using System.Reactive.Subjects;
using Avalonia.Platform.Storage;
using DynamicData;
using DynamicData.Alias;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NexusMods.Abstractions.FileStore;
using NexusMods.Abstractions.FileStore.ArchiveMetadata;
using NexusMods.Abstractions.FileStore.Downloads;
using NexusMods.Abstractions.Games.DTO;
using NexusMods.Abstractions.Installers;
using NexusMods.Abstractions.Library;
Expand All @@ -26,7 +25,6 @@
using NexusMods.CrossPlatform.Process;
using NexusMods.Icons;
using NexusMods.MnemonicDB.Abstractions;
using NexusMods.Networking.Downloaders.Tasks.State;
using NexusMods.Paths;
using ReactiveUI;

Expand Down Expand Up @@ -133,18 +131,19 @@ public FileOriginsPageViewModel(
workspaceController.OpenPage(workspaceController.ActiveWorkspaceId, pageData, behavior);
}
);
LibraryArchive.ObserveAll(_conn)
.Filter(model => FilterDownloadAnalysisModel(model, game.Domain))
LibraryUserFilters.ObserveFilteredLibraryItems(connection: _conn)
.Select(item => item.ToLibraryFile())
.Where(file => file.IsValid())
.OnUI()
.Transform(fileOrigin => (IFileOriginEntryViewModel)
.Transform(libraryFile => (IFileOriginEntryViewModel)
new FileOriginEntryViewModel(
_conn,
LoadoutId,
fileOrigin,
libraryFile,
viewModCommand,
ReactiveCommand.CreateFromTask(async () => await AddUsingInstallerToLoadout(fileOrigin, null, default(CancellationToken))),
ReactiveCommand.CreateFromTask(async () => await AddUsingInstallerToLoadout(fileOrigin, advancedInstaller, default(CancellationToken)))
ReactiveCommand.CreateFromTask(async () => await AddUsingInstallerToLoadout(libraryFile.AsLibraryItem(), null, default(CancellationToken))),
ReactiveCommand.CreateFromTask(async () => await AddUsingInstallerToLoadout(libraryFile.AsLibraryItem(), advancedInstaller, default(CancellationToken)))
)
)
.Bind(out _fileOrigins)
Expand All @@ -166,12 +165,6 @@ public FileOriginsPageViewModel(
});
}

public static bool FilterDownloadAnalysisModel(LibraryArchive.ReadOnly model, GameDomain currentGameDomain)
{
// TODO: Filter by game domain
return true;
}

public async Task RegisterFromDisk(IStorageProvider storageProvider)
{
var files = await PickModFiles(storageProvider);
Expand Down Expand Up @@ -207,13 +200,13 @@ public async Task OpenNexusModPage()
private async Task DoAddModImpl(ILibraryItemInstaller? installer, CancellationToken token)
{
foreach (var mod in SelectedModsCollection)
await AddUsingInstallerToLoadout(mod.FileOrigin, installer, token);
await AddUsingInstallerToLoadout(mod.LibraryFile.AsLibraryItem(), installer, token);
}

private async Task AddUsingInstallerToLoadout(LibraryArchive.ReadOnly fileOrigin, ILibraryItemInstaller? installer, CancellationToken token)
private async Task AddUsingInstallerToLoadout(LibraryItem.ReadOnly libraryItem, ILibraryItemInstaller? installer, CancellationToken token)
{
var loadout = Loadout.Load(_conn.Db, LoadoutId);
await using var job = _libraryService.InstallItem(fileOrigin.AsLibraryFile().AsLibraryItem(), loadout, installer);
await using var job = _libraryService.InstallItem(libraryItem, loadout, installer);
await job.StartAsync(token);
await job.WaitToFinishAsync(token);
}
Expand Down
Loading

0 comments on commit 8847c94

Please sign in to comment.