Skip to content

Commit

Permalink
Merge pull request #1617 from Nexus-Mods/fix/1591-1589-empty-state
Browse files Browse the repository at this point in the history
Empty Library and Modlist messages
  • Loading branch information
erri120 authored Jun 17, 2024
2 parents 94831c2 + 73d57eb commit 6211646
Show file tree
Hide file tree
Showing 17 changed files with 327 additions and 87 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
using System.Collections.ObjectModel;
using System.Reactive;
using NexusMods.Abstractions.Loadouts;
using NexusMods.Abstractions.Loadouts.Ids;
using NexusMods.Abstractions.Loadouts.Mods;
using NexusMods.App.UI.Controls.DataGrid;
using NexusMods.App.UI.Controls.MarkdownRenderer;
using NexusMods.App.UI.Controls.Navigation;
using NexusMods.App.UI.WorkspaceSystem;
using ReactiveUI;
Expand All @@ -18,15 +15,16 @@ public interface ILoadoutGridViewModel : IPageViewModelInterface
{
public ReadOnlyObservableCollection<ModId> Mods { get; }
public LoadoutId LoadoutId { get; set; }
public string LoadoutName { get; }

public IMarkdownRendererViewModel MarkdownRendererViewModel { get; }
public string? EmptyModlistTitleMessage { get; }

public ReadOnlyObservableCollection<IDataGridColumnFactory<LoadoutColumn>> Columns { get; }

public ModId[] SelectedItems { get; set; }

public ReactiveCommand<NavigationInformation, Unit> ViewModContentsCommand { get; }

public ReactiveCommand<NavigationInformation, Unit> ViewModLibraryCommand { get; }

/// <summary>
/// Delete the mods from the loadout.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ public class LoadoutGridDesignViewModel(IWindowManager windowManager) : APageVie
public LoadoutId LoadoutId { get; set; }
public string LoadoutName { get; } = "Design Loadout";
public IMarkdownRendererViewModel MarkdownRendererViewModel { get; } = new MarkdownRendererDesignViewModel();
public string EmptyModlistTitleMessage { get; } = "No mods added to Game";
public ReadOnlyObservableCollection<IDataGridColumnFactory<LoadoutColumn>> Columns { get; } = new([]);
public ModId[] SelectedItems { get; set; } = [];
public ReactiveCommand<NavigationInformation, Unit> ViewModContentsCommand { get; } = ReactiveCommand.Create<NavigationInformation, Unit>(_ => Unit.Default);
public ReactiveCommand<NavigationInformation, Unit> ViewModLibraryCommand { get; } = ReactiveCommand.Create<NavigationInformation, Unit>(_ => Unit.Default);

public LoadoutGridDesignViewModel() : this(new DesignWindowManager()) { }

Expand Down
49 changes: 38 additions & 11 deletions src/NexusMods.App.UI/Pages/LoadoutGrid/LoadoutGridView.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,35 +20,62 @@
<Grid RowDefinitions="Auto, *">
<Border Grid.Row="0" Classes="Toolbar">
<StackPanel>
<Line/>
<Line />

<navigation:NavigationControl x:Name="ViewModFilesButton">
<StackPanel>
<icons:UnifiedIcon Classes="FolderOutline" />
<TextBlock Text="{x:Static resources:Language.LoadoutGridView__View_Files}"/>
<TextBlock Text="{x:Static resources:Language.LoadoutGridView__View_Files}" />
</StackPanel>
</navigation:NavigationControl>
<Button x:Name="DeleteModsButton">
<StackPanel>
<icons:UnifiedIcon Classes="Close" />
<TextBlock Text="{x:Static resources:Language.LoadoutGridView__Remove}"/>
<TextBlock Text="{x:Static resources:Language.LoadoutGridView__Remove}" />
</StackPanel>
</Button>
</StackPanel>
</Border>

<DataGrid Grid.Row="1"
Classes="NoTopBorder"
CanUserResizeColumns="True"
CanUserSortColumns="True"
x:Name="ModsDataGrid"/>
x:Name="ModsDataGrid" />

<Border Grid.Row="1"
x:Name="EmptyModlistMessageBorder"
Margin="24,68,24,24"
Padding="24,64,24,64"
IsVisible="False">
<markdownRenderer:MarkdownRendererView x:Name="EmptyModlistMessageMarkdownViewer"/>
<StackPanel Orientation="Vertical"
HorizontalAlignment="Center"
Classes="Spacing-2">

<TextBlock Classes="BodyXLBold ForegroundSubdued"
HorizontalAlignment="Center"
x:Name="EmptyModlistTitleTextBlock" />

<StackPanel Orientation="Horizontal"
Classes="Spacing-1_5"
HorizontalAlignment="Center">

<TextBlock Classes="BodyLGNormal ForegroundSubdued"
VerticalAlignment="Center"
Text="{x:Static resources:Language.LoadoutGrid_EmptyModlistSubtitle_Add_from_library}" />
<navigation:NavigationControl Classes="Square Primary"
x:Name="ViewModLibraryButton">
<StackPanel Orientation="Horizontal" Classes="Spacing-1">
<icons:UnifiedIcon Classes="ModLibrary ForegroundSubdued"
VerticalAlignment="Center" Size="18"/>
<TextBlock Classes="BodyLGNormal ForegroundSubdued"
VerticalAlignment="Center"
Text="Library" />
</StackPanel>
</navigation:NavigationControl>

</StackPanel>
</StackPanel>
</Border>

</Grid>
</reactiveUi:ReactiveUserControl>

16 changes: 12 additions & 4 deletions src/NexusMods.App.UI/Pages/LoadoutGrid/LoadoutGridView.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ public LoadoutGridView()
InitializeComponent();
this.WhenActivated(d =>
{
this.OneWayBind(ViewModel, vm => vm.MarkdownRendererViewModel,
view => view.EmptyModlistMessageMarkdownViewer.ViewModel)
this.OneWayBind(ViewModel, vm => vm.EmptyModlistTitleMessage,
view => view.EmptyModlistTitleTextBlock.Text)
.DisposeWith(d);

this.WhenAnyValue(view => view.ViewModel!.Mods)
Expand All @@ -29,6 +29,9 @@ public LoadoutGridView()

this.BindCommand(ViewModel, vm => vm.ViewModContentsCommand, view => view.ViewModFilesButton)
.DisposeWith(d);

this.BindCommand(ViewModel, vm => vm.ViewModLibraryCommand, view => view.ViewModLibraryButton)
.DisposeWith(d);

this.WhenAnyValue(view => view.ViewModel!.Columns)
.GenerateColumns(ModsDataGrid)
Expand All @@ -42,8 +45,13 @@ public LoadoutGridView()
.DisposeWith(d);

this.WhenAnyValue(view => view.ViewModel!.Mods.Count)
.Select(count => count == 0)
.BindTo(this, view => view.EmptyModlistMessageBorder.IsVisible)
.Select(count => count > 0)
.Subscribe(hasItems =>
{
ModsDataGrid.IsVisible = hasItems;
EmptyModlistMessageBorder.IsVisible = !hasItems;
}
)
.DisposeWith(d);

// TODO: remove these commands and move all of this into the ViewModel
Expand Down
40 changes: 26 additions & 14 deletions src/NexusMods.App.UI/Pages/LoadoutGrid/LoadoutGridViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NexusMods.Abstractions.GameLocators;
using NexusMods.Abstractions.Loadouts;
using NexusMods.Abstractions.Loadouts.Ids;
using NexusMods.Abstractions.Loadouts.Mods;
using NexusMods.Abstractions.MnemonicDB.Attributes;
using NexusMods.Abstractions.Settings;
using NexusMods.Abstractions.Telemetry;
using NexusMods.App.UI.Controls.DataGrid;
using NexusMods.App.UI.Controls.MarkdownRenderer;
using NexusMods.App.UI.Controls.Navigation;
using NexusMods.App.UI.Pages.LoadoutGrid.Columns.ModCategory;
using NexusMods.App.UI.Pages.LoadoutGrid.Columns.ModEnabled;
Expand All @@ -23,6 +22,7 @@
using NexusMods.App.UI.Pages.LoadoutGrid.Columns.ModVersion;
using NexusMods.App.UI.Pages.ModInfo;
using NexusMods.App.UI.Pages.ModInfo.Types;
using NexusMods.App.UI.Pages.ModLibrary;
using NexusMods.App.UI.Resources;
using NexusMods.App.UI.Settings;
using NexusMods.App.UI.Windows;
Expand All @@ -48,14 +48,16 @@ public class LoadoutGridViewModel : APageViewModel<ILoadoutGridViewModel>, ILoad

private ReadOnlyObservableCollection<IDataGridColumnFactory<LoadoutColumn>> _filteredColumns = new([]);

public IMarkdownRendererViewModel MarkdownRendererViewModel { get; }

[Reactive] public string? EmptyModlistTitleMessage { get; private set; }
public ReadOnlyObservableCollection<IDataGridColumnFactory<LoadoutColumn>> Columns => _filteredColumns;

[Reactive] public LoadoutId LoadoutId { get; set; }
[Reactive] public string LoadoutName { get; set; } = "";

[Reactive] public ModId[] SelectedItems { get; set; } = [];
public ReactiveCommand<NavigationInformation, Unit> ViewModContentsCommand { get; }
public ReactiveCommand<NavigationInformation, Unit> ViewModLibraryCommand { get; }

public LoadoutGridViewModel() : base(null!)
{
Expand All @@ -71,8 +73,6 @@ public LoadoutGridViewModel(
{
_conn = conn;

MarkdownRendererViewModel = provider.GetRequiredService<IMarkdownRendererViewModel>();

_columns = new SourceCache<IDataGridColumnFactory<LoadoutColumn>, LoadoutColumn>(_ => throw new NotSupportedException());
_mods = new ReadOnlyObservableCollection<ModId>(new ObservableCollection<ModId>());

Expand Down Expand Up @@ -125,6 +125,23 @@ public LoadoutGridViewModel(
var behavior = workspaceController.GetOpenPageBehavior(pageData, info, IdBundle);
workspaceController.OpenPage(WorkspaceId, pageData, behavior);
}, hasSelection);

ViewModLibraryCommand = ReactiveCommand.Create<NavigationInformation>(info =>
{

var pageData = new PageData
{
Context = new FileOriginsPageContext()
{
LoadoutId = LoadoutId,
},
FactoryId = FileOriginsPageFactory.StaticId,
};

var workspaceController = GetWorkspaceController();
var behavior = workspaceController.GetOpenPageBehavior(pageData, info, IdBundle);
workspaceController.OpenPage(WorkspaceId, pageData, behavior);
});

this.WhenActivated(d =>
{
Expand Down Expand Up @@ -159,7 +176,7 @@ public LoadoutGridViewModel(
.WhereNotNull()
.SubscribeWithErrorLogging(loadout =>
{
MarkdownRendererViewModel.Contents = GetEmptyModlistMarkdownString();
EmptyModlistTitleMessage = GetEmptyModlistTitleString(loadout.Installation);
})
.DisposeWith(d);

Expand All @@ -186,14 +203,9 @@ public async Task DeleteMods(IEnumerable<ModId> modsToDelete, string commitMessa
}

private const string NexusModsUrl = "https://www.nexusmods.com/{0}";
private string GetEmptyModlistMarkdownString()
private static string GetEmptyModlistTitleString(GameInstallation gameInstallation)
{
var gameDomain = _conn.Db.Get(LoadoutId).Installation.Game.Domain;
var url = NexusModsUrlBuilder.CreateGenericUri(string.Format(NexusModsUrl, gameDomain));
const string mkString = """
### No mods have been added
View and add your existing downloaded mods from the **Library** or [browse new mods on Nexus Mods]({0})
""";
return string.Format(mkString, url);
return string.Format(Language.LoadoutGridViewModel_EmptyModlistTitleString, gameInstallation.Game.Name);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
</Border>

<DataGrid Grid.Row="1"
Classes="NoTopBorder"
CanUserReorderColumns="True"
CanUserResizeColumns="True"
CanUserSortColumns="True"
Expand Down Expand Up @@ -148,6 +149,37 @@

</DataGrid.Columns>
</DataGrid>

<!-- Empty message -->
<Border Grid.Row="1"
x:Name="EmptyLibraryMessageBorder"
Padding="24,64,24,64"
IsVisible="False">
<StackPanel Orientation="Vertical"
HorizontalAlignment="Center"
Classes="Spacing-2">

<TextBlock Classes="BodyXLBold ForegroundSubdued"
HorizontalAlignment="Center"
Text="{x:Static resources:Language.EmptyLibraryTitleText}" />

<StackPanel Orientation="Horizontal"
Classes="Spacing-1"
HorizontalAlignment="Center">

<Button Classes="Hyperlink Secondary"
x:Name="EmptyLibraryLinkButton">
<TextBlock Classes="BodyLGNormal ForegroundSubdued"
x:Name="EmptyLibrarySubtitleTextBlock"/>
</Button>
<Button Classes="BareIcon OpenInNew ForegroundSubdued"
Height="18"
Width="18"
x:Name="OpenLinkBareIconButton"/>

</StackPanel>
</StackPanel>
</Border>

</Grid>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Reactive.Disposables;
using System.Reactive.Linq;
using Avalonia.Controls;
using Avalonia.ReactiveUI;
using NexusMods.App.UI.Helpers;
Expand Down Expand Up @@ -33,6 +34,25 @@ public FileOriginsPageView()
this.BindCommand(ViewModel, vm => vm.OpenNexusModPage, view => view.GetModsFromNexusButton)
.DisposeWith(d);

this.BindCommand(ViewModel, vm => vm.OpenNexusModPage, view => view.EmptyLibraryLinkButton)
.DisposeWith(d);

this.BindCommand(ViewModel, vm => vm.OpenNexusModPage, view => view.OpenLinkBareIconButton)
.DisposeWith(d);

this.WhenAnyValue(view => view.ViewModel!.FileOrigins.Count)
.Select(count => count > 0)
.Subscribe(hasItems =>
{
DataGrid.IsVisible = hasItems;
EmptyLibraryMessageBorder.IsVisible = !hasItems;
})
.DisposeWith(d);

this.OneWayBind(ViewModel, vm => vm.EmptyLibrarySubtitleText,
view => view.EmptyLibrarySubtitleTextBlock.Text)
.DisposeWith(d);

// Note: We get `StorageProvider` from Avalonia, using the View TopLevel.
// This is the suggested approach by an Avalonia team member.
// https://github.com/AvaloniaUI/Avalonia/discussions/10227
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ public IObservable<IChangeSet<IFileOriginEntryViewModel, IFileOriginEntryViewMod
}

public ReadOnlyObservableCollection<IFileOriginEntryViewModel> SelectedModsCollection => _selectedModsCollection;

public string EmptyLibrarySubtitleText { get; }

public ReactiveCommand<Unit, Unit> AddMod { get; private set; } = null!;
public ReactiveCommand<Unit, Unit> AddModAdvanced { get; private set; } = null!;
Expand Down Expand Up @@ -104,6 +106,8 @@ public FileOriginsPageViewModel(
await DoAddModImpl(advancedInstaller, cancellationToken);
}, canAddMod);

EmptyLibrarySubtitleText = string.Format(Language.FileOriginsPageViewModel_EmptyLibrarySubtitleText, game.Name);

this.WhenActivated(d =>
{
var workspaceController = GetWorkspaceController();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ public interface IFileOriginsPageViewModel : IPageViewModelInterface
IObservable<IChangeSet<IFileOriginEntryViewModel, IFileOriginEntryViewModel>> SelectedModsObservable { get; [UsedImplicitly] set; }

ReadOnlyObservableCollection<IFileOriginEntryViewModel> SelectedModsCollection { get; }

string EmptyLibrarySubtitleText { get; }

/// <summary>
/// Add a mod to the loadout using the standard installer.
Expand Down
Loading

0 comments on commit 6211646

Please sign in to comment.