Skip to content

Commit a939620

Browse files
authored
Merge pull request #3832 from Nexus-Mods/downloads-page-data-layer
Downloads Page Backend: First Bigger Half
2 parents 9d1f0d6 + ee556f8 commit a939620

File tree

10 files changed

+753
-10
lines changed

10 files changed

+753
-10
lines changed

src/NexusMods.App.UI/Controls/GameWidget/GameWidgetViewModel.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using NexusMods.Abstractions.Games.FileHashes;
99
using NexusMods.Abstractions.Settings;
1010
using NexusMods.Abstractions.UI;
11+
using NexusMods.App.UI.Resources;
1112
using NexusMods.UI.Sdk.Icons;
1213
using ReactiveUI;
1314
using ReactiveUI.Fody.Helpers;
@@ -47,7 +48,7 @@ public GameWidgetViewModel(ILogger<GameWidgetViewModel> logger, ISettingsManager
4748
await fileHashesService.GetFileHashesDb();
4849
var locatorIds = installation.LocatorResultMetadata?.ToLocatorIds().ToArray() ?? [];
4950
if (!fileHashesService.TryGetVanityVersion((installation.Store, locatorIds), out var vanityVersion))
50-
return "Version: Unknown";
51+
return Language.GameWidget_VersionUnknown;
5152
return $"Version: {vanityVersion.Value}";
5253
})
5354
.BindToVM(this, vm => vm.Version)
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
using NexusMods.App.UI.Controls;
2+
3+
namespace NexusMods.App.UI.Pages.Downloads;
4+
5+
/*
6+
* DOWNLOADS COLUMN DEFINITIONS OVERVIEW
7+
* ====================================
8+
*
9+
* This file defines the column structure for the Downloads TreeDataGrid.
10+
* Each column implements ICompositeColumnDefinition<T> and maps to specific components.
11+
*
12+
* COLUMN STRUCTURE (5 columns total):
13+
*
14+
* 1. NAME+ICON COLUMN:
15+
* - Uses SharedColumns.Name from Controls/TreeDataGrid/SharedColumns.cs
16+
* - Components: NameComponent + ImageComponent
17+
* - Shows download/mod name with icon
18+
*
19+
* 2. GAME COLUMN (defined here as DownloadColumns.Game):
20+
* - Maps to: DownloadComponents.GameComponent (from DownloadComponents.cs)
21+
* - Shows: Game name + game icon
22+
* - Sortable by game name alphabetically
23+
*
24+
* 3. SIZE COLUMN (defined here as DownloadColumns.Size):
25+
* - Maps to: DownloadComponents.SizeProgressComponent (from DownloadComponents.cs)
26+
* - Format: "15.6MB of 56MB" (downloaded of total)
27+
* - Sortable by total file size
28+
*
29+
* 4. SPEED COLUMN (defined here as DownloadColumns.Speed):
30+
* - Maps to: DownloadComponents.SpeedComponent (from DownloadComponents.cs)
31+
* - Shows: Transfer rate "5.2 MB/s" or "--"
32+
* - Sortable by current transfer rate
33+
*
34+
* 5. STATUS COLUMN (defined here as DownloadColumns.Status):
35+
* - Maps to: DownloadComponents.StatusComponent (from DownloadComponents.cs)
36+
* - Contains: Progress bar + Pause/Resume + Cancel + Kebab menu
37+
* - Consolidated all download actions into single column
38+
* - Sortable by completion percentage (0% to 100%)
39+
*/
40+
41+
/// <summary>
42+
/// Defines column template resource keys and component keys for Downloads.
43+
/// </summary>
44+
public static class DownloadColumns
45+
{
46+
/// <summary>Displays game name with game icon, sorted alphabetically by name.</summary>
47+
public sealed class Game : ICompositeColumnDefinition<Game>
48+
{
49+
public static int Compare<TKey>(CompositeItemModel<TKey> a, CompositeItemModel<TKey> b) where TKey : notnull
50+
{
51+
var aValue = a.GetOptional<DownloadComponents.GameComponent>(ComponentKey);
52+
var bValue = b.GetOptional<DownloadComponents.GameComponent>(ComponentKey);
53+
return (aValue.HasValue, bValue.HasValue) switch
54+
{
55+
(true, true) => aValue.Value.CompareTo(bValue.Value),
56+
(true, false) => 1,
57+
(false, true) => -1,
58+
(false, false) => 0,
59+
};
60+
}
61+
62+
public const string ColumnTemplateResourceKey = nameof(DownloadColumns) + "_" + nameof(Game);
63+
public static readonly ComponentKey ComponentKey = ComponentKey.From(ColumnTemplateResourceKey + "_" + nameof(DownloadComponents.GameComponent));
64+
65+
public static string GetColumnHeader() => "Game";
66+
public static string GetColumnTemplateResourceKey() => ColumnTemplateResourceKey;
67+
}
68+
69+
/// <summary>Displays size as "15.6 MB of 56 MB" format, sorted by total file size.</summary>
70+
public sealed class Size : ICompositeColumnDefinition<Size>
71+
{
72+
public static int Compare<TKey>(CompositeItemModel<TKey> a, CompositeItemModel<TKey> b) where TKey : notnull
73+
{
74+
var aValue = a.GetOptional<DownloadComponents.SizeProgressComponent>(ComponentKey);
75+
var bValue = b.GetOptional<DownloadComponents.SizeProgressComponent>(ComponentKey);
76+
return (aValue.HasValue, bValue.HasValue) switch
77+
{
78+
(true, true) => aValue.Value.CompareTo(bValue.Value),
79+
(true, false) => 1,
80+
(false, true) => -1,
81+
(false, false) => 0,
82+
};
83+
}
84+
85+
public const string ColumnTemplateResourceKey = nameof(DownloadColumns) + "_" + nameof(Size);
86+
public static readonly ComponentKey ComponentKey = ComponentKey.From(ColumnTemplateResourceKey + "_" + nameof(DownloadComponents.SizeProgressComponent));
87+
88+
public static string GetColumnHeader() => "Size";
89+
public static string GetColumnTemplateResourceKey() => ColumnTemplateResourceKey;
90+
}
91+
92+
/// <summary>Shows transfer rate as "5.2 MB/s" or "--" when inactive, sorted by speed.</summary>
93+
public sealed class Speed : ICompositeColumnDefinition<Speed>
94+
{
95+
public static int Compare<TKey>(CompositeItemModel<TKey> a, CompositeItemModel<TKey> b) where TKey : notnull
96+
{
97+
var aValue = a.GetOptional<DownloadComponents.SpeedComponent>(ComponentKey);
98+
var bValue = b.GetOptional<DownloadComponents.SpeedComponent>(ComponentKey);
99+
return (aValue.HasValue, bValue.HasValue) switch
100+
{
101+
(true, true) => aValue.Value.CompareTo(bValue.Value),
102+
(true, false) => 1,
103+
(false, true) => -1,
104+
(false, false) => 0,
105+
};
106+
}
107+
108+
public const string ColumnTemplateResourceKey = nameof(DownloadColumns) + "_" + nameof(Speed);
109+
public static readonly ComponentKey ComponentKey = ComponentKey.From(ColumnTemplateResourceKey + "_" + nameof(DownloadComponents.SpeedComponent));
110+
111+
public static string GetColumnHeader() => "Speed";
112+
public static string GetColumnTemplateResourceKey() => ColumnTemplateResourceKey;
113+
}
114+
115+
/// <summary>Contains progress bar, pause/resume, cancel buttons, and kebab menu, sorted by completion %.</summary>
116+
public sealed class Status : ICompositeColumnDefinition<Status>
117+
{
118+
public static int Compare<TKey>(CompositeItemModel<TKey> a, CompositeItemModel<TKey> b) where TKey : notnull
119+
{
120+
var aValue = a.GetOptional<DownloadComponents.StatusComponent>(ComponentKey);
121+
var bValue = b.GetOptional<DownloadComponents.StatusComponent>(ComponentKey);
122+
return (aValue.HasValue, bValue.HasValue) switch
123+
{
124+
(true, true) => aValue.Value.Progress.Value.CompareTo(bValue.Value.Progress.Value),
125+
(true, false) => 1,
126+
(false, true) => -1,
127+
(false, false) => 0,
128+
};
129+
}
130+
131+
public const string ColumnTemplateResourceKey = nameof(DownloadColumns) + "_" + nameof(Status);
132+
public static readonly ComponentKey ComponentKey = ComponentKey.From(ColumnTemplateResourceKey + "_" + nameof(DownloadComponents.StatusComponent));
133+
134+
public static string GetColumnHeader() => "Status";
135+
public static string GetColumnTemplateResourceKey() => ColumnTemplateResourceKey;
136+
}
137+
138+
public static readonly ComponentKey DownloadRefComponentKey = ComponentKey.From("Downloads_DownloadRef");
139+
}

0 commit comments

Comments
 (0)