Skip to content

Commit 22d06e0

Browse files
CopilotLeftofZen
andcommitted
Implement all requested metadata editing features
1. Made Availability and InternalName readonly (removed editable ComboBox for Availability) 2. Made Licence editable as ComboBox with values from /licences endpoint, includes "None" option 3. Added UI for editing Authors, Tags, and ObjectPacks with add/remove functionality 4. Added ReactiveCommands for add/remove operations 5. Fetch available Authors, Tags, ObjectPacks, and Licences from server 6. Sync collections back to Metadata model when changed Co-authored-by: LeftofZen <7483209+LeftofZen@users.noreply.github.com>
1 parent 2c7e1c6 commit 22d06e0

5 files changed

Lines changed: 382 additions & 60 deletions

File tree

Definitions/Web/Client.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,36 @@ public static async Task<IEnumerable<DtoObjectEntry>> GetObjectListAsync(HttpCli
6363
entry,
6464
logger);
6565
}
66+
67+
public static async Task<IEnumerable<DtoLicenceEntry>> GetLicencesAsync(HttpClient client, ILogger? logger = null)
68+
=> await ClientHelpers.GetAsync<IEnumerable<DtoLicenceEntry>>(
69+
client,
70+
ApiVersion,
71+
RoutesV2.Licences,
72+
null,
73+
logger) ?? [];
74+
75+
public static async Task<IEnumerable<DtoAuthorEntry>> GetAuthorsAsync(HttpClient client, ILogger? logger = null)
76+
=> await ClientHelpers.GetAsync<IEnumerable<DtoAuthorEntry>>(
77+
client,
78+
ApiVersion,
79+
RoutesV2.Authors,
80+
null,
81+
logger) ?? [];
82+
83+
public static async Task<IEnumerable<DtoTagEntry>> GetTagsAsync(HttpClient client, ILogger? logger = null)
84+
=> await ClientHelpers.GetAsync<IEnumerable<DtoTagEntry>>(
85+
client,
86+
ApiVersion,
87+
RoutesV2.Tags,
88+
null,
89+
logger) ?? [];
90+
91+
public static async Task<IEnumerable<DtoItemPackEntry>> GetObjectPacksAsync(HttpClient client, ILogger? logger = null)
92+
=> await ClientHelpers.GetAsync<IEnumerable<DtoItemPackEntry>>(
93+
client,
94+
ApiVersion,
95+
RoutesV2.ObjectPacks,
96+
null,
97+
logger) ?? [];
6698
}

Gui/ObjectServiceClient.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,16 @@ public async Task<IEnumerable<DtoObjectEntry>> GetObjectListAsync()
7575

7676
public async Task<DtoObjectMissingEntry?> AddMissingObjectAsync(DtoObjectMissingUpload entry)
7777
=> await Client.AddMissingObjectAsync(WebClient, entry, Logger);
78+
79+
public async Task<IEnumerable<DtoLicenceEntry>> GetLicencesAsync()
80+
=> await Client.GetLicencesAsync(WebClient, Logger);
81+
82+
public async Task<IEnumerable<DtoAuthorEntry>> GetAuthorsAsync()
83+
=> await Client.GetAuthorsAsync(WebClient, Logger);
84+
85+
public async Task<IEnumerable<DtoTagEntry>> GetTagsAsync()
86+
=> await Client.GetTagsAsync(WebClient, Logger);
87+
88+
public async Task<IEnumerable<DtoItemPackEntry>> GetObjectPacksAsync()
89+
=> await Client.GetObjectPacksAsync(WebClient, Logger);
7890
}

Gui/ViewModels/LocoTypes/ObjectEditorViewModel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ public override void Load()
307307

308308
if (CurrentObject?.Metadata != null)
309309
{
310-
MetadataViewModel = new ObjectMetadataViewModel(CurrentObject.Metadata);
310+
MetadataViewModel = new ObjectMetadataViewModel(CurrentObject.Metadata, Model.ObjectServiceClient);
311311
}
312312
else
313313
{

Gui/ViewModels/ObjectMetadataViewModel.cs

Lines changed: 180 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,86 @@
11
using Definitions;
2+
using Definitions.DTO;
23
using Definitions.ObjectModels;
34
using ReactiveUI;
45
using System;
56
using System.Collections.Generic;
7+
using System.Collections.ObjectModel;
68
using System.Linq;
9+
using System.Reactive;
10+
using System.Threading.Tasks;
711

812
namespace Gui.ViewModels;
913

1014
public class ObjectMetadataViewModel : ReactiveObject
1115
{
12-
public ObjectMetadataViewModel(ObjectMetadata metadata)
16+
readonly Gui.ObjectServiceClient? objectServiceClient;
17+
18+
public ObjectMetadataViewModel(ObjectMetadata metadata, Gui.ObjectServiceClient? objectServiceClient = null)
1319
{
1420
Metadata = metadata;
1521
description = metadata.Description;
16-
availability = metadata.Availability;
1722
createdDate = metadata.CreatedDate;
1823
modifiedDate = metadata.ModifiedDate;
24+
selectedLicence = metadata.Licence;
25+
26+
// Initialize observable collections from metadata
27+
Authors = new ObservableCollection<DtoAuthorEntry>(metadata.Authors);
28+
Tags = new ObservableCollection<DtoTagEntry>(metadata.Tags);
29+
ObjectPacks = new ObservableCollection<DtoItemPackEntry>(metadata.ObjectPacks);
30+
31+
this.objectServiceClient = objectServiceClient;
32+
33+
// Initialize commands
34+
AddAuthorCommand = ReactiveCommand.Create<DtoAuthorEntry?>(author =>
35+
{
36+
if (author != null && !Authors.Contains(author))
37+
{
38+
Authors.Add(author);
39+
SyncAuthorsToMetadata();
40+
}
41+
});
42+
43+
RemoveAuthorCommand = ReactiveCommand.Create<DtoAuthorEntry>(author =>
44+
{
45+
Authors.Remove(author);
46+
SyncAuthorsToMetadata();
47+
});
48+
49+
AddTagCommand = ReactiveCommand.Create<DtoTagEntry?>(tag =>
50+
{
51+
if (tag != null && !Tags.Contains(tag))
52+
{
53+
Tags.Add(tag);
54+
SyncTagsToMetadata();
55+
}
56+
});
57+
58+
RemoveTagCommand = ReactiveCommand.Create<DtoTagEntry>(tag =>
59+
{
60+
Tags.Remove(tag);
61+
SyncTagsToMetadata();
62+
});
63+
64+
AddObjectPackCommand = ReactiveCommand.Create<DtoItemPackEntry?>(pack =>
65+
{
66+
if (pack != null && !ObjectPacks.Contains(pack))
67+
{
68+
ObjectPacks.Add(pack);
69+
SyncObjectPacksToMetadata();
70+
}
71+
});
72+
73+
RemoveObjectPackCommand = ReactiveCommand.Create<DtoItemPackEntry>(pack =>
74+
{
75+
ObjectPacks.Remove(pack);
76+
SyncObjectPacksToMetadata();
77+
});
78+
79+
// Load data from server if we have a client
80+
if (objectServiceClient != null)
81+
{
82+
_ = LoadServerDataAsync();
83+
}
1984
}
2085

2186
public ObjectMetadataViewModel() : this(new ObjectMetadata("<empty>"))
@@ -27,8 +92,114 @@ public ObjectMetadataViewModel() : this(new ObjectMetadata("<empty>"))
2792
// InternalName is readonly (init-only in the model)
2893
public string InternalName => Metadata.InternalName;
2994

30-
// Available values for Availability enum
31-
public IEnumerable<ObjectAvailability> AvailabilityValues => Enum.GetValues<ObjectAvailability>();
95+
// Availability is readonly (user cannot change this)
96+
public ObjectAvailability Availability => Metadata.Availability;
97+
98+
// Collections for editing
99+
public ObservableCollection<DtoAuthorEntry> Authors { get; }
100+
public ObservableCollection<DtoTagEntry> Tags { get; }
101+
public ObservableCollection<DtoItemPackEntry> ObjectPacks { get; }
102+
103+
// Commands
104+
public ReactiveCommand<DtoAuthorEntry?, Unit> AddAuthorCommand { get; }
105+
public ReactiveCommand<DtoAuthorEntry, Unit> RemoveAuthorCommand { get; }
106+
public ReactiveCommand<DtoTagEntry?, Unit> AddTagCommand { get; }
107+
public ReactiveCommand<DtoTagEntry, Unit> RemoveTagCommand { get; }
108+
public ReactiveCommand<DtoItemPackEntry?, Unit> AddObjectPackCommand { get; }
109+
public ReactiveCommand<DtoItemPackEntry, Unit> RemoveObjectPackCommand { get; }
110+
111+
// Available items for selection
112+
ObservableCollection<DtoAuthorEntry> availableAuthors = [];
113+
public ObservableCollection<DtoAuthorEntry> AvailableAuthors
114+
{
115+
get => availableAuthors;
116+
set => this.RaiseAndSetIfChanged(ref availableAuthors, value);
117+
}
118+
119+
ObservableCollection<DtoTagEntry> availableTags = [];
120+
public ObservableCollection<DtoTagEntry> AvailableTags
121+
{
122+
get => availableTags;
123+
set => this.RaiseAndSetIfChanged(ref availableTags, value);
124+
}
125+
126+
ObservableCollection<DtoItemPackEntry> availableObjectPacks = [];
127+
public ObservableCollection<DtoItemPackEntry> AvailableObjectPacks
128+
{
129+
get => availableObjectPacks;
130+
set => this.RaiseAndSetIfChanged(ref availableObjectPacks, value);
131+
}
132+
133+
// Available licences
134+
ObservableCollection<DtoLicenceEntry?> availableLicences = [];
135+
public ObservableCollection<DtoLicenceEntry?> AvailableLicences
136+
{
137+
get => availableLicences;
138+
set => this.RaiseAndSetIfChanged(ref availableLicences, value);
139+
}
140+
141+
async Task LoadServerDataAsync()
142+
{
143+
if (objectServiceClient == null)
144+
{
145+
return;
146+
}
147+
148+
try
149+
{
150+
// Load licences
151+
var licences = await objectServiceClient.GetLicencesAsync();
152+
var licenceList = new List<DtoLicenceEntry?> { null }; // Add None option
153+
licenceList.AddRange(licences);
154+
AvailableLicences = new ObservableCollection<DtoLicenceEntry?>(licenceList);
155+
156+
// Load authors, tags, and object packs
157+
var authors = await objectServiceClient.GetAuthorsAsync();
158+
AvailableAuthors = new ObservableCollection<DtoAuthorEntry>(authors);
159+
160+
var tags = await objectServiceClient.GetTagsAsync();
161+
AvailableTags = new ObservableCollection<DtoTagEntry>(tags);
162+
163+
var objectPacks = await objectServiceClient.GetObjectPacksAsync();
164+
AvailableObjectPacks = new ObservableCollection<DtoItemPackEntry>(objectPacks);
165+
}
166+
catch (Exception)
167+
{
168+
// If we can't load data (e.g., offline mode), just set empty lists
169+
AvailableLicences = new ObservableCollection<DtoLicenceEntry?> { null };
170+
AvailableAuthors = new ObservableCollection<DtoAuthorEntry>();
171+
AvailableTags = new ObservableCollection<DtoTagEntry>();
172+
AvailableObjectPacks = new ObservableCollection<DtoItemPackEntry>();
173+
}
174+
}
175+
176+
// Commands for adding/removing items
177+
void SyncAuthorsToMetadata()
178+
{
179+
Metadata.Authors.Clear();
180+
foreach (var author in Authors)
181+
{
182+
Metadata.Authors.Add(author);
183+
}
184+
}
185+
186+
void SyncTagsToMetadata()
187+
{
188+
Metadata.Tags.Clear();
189+
foreach (var tag in Tags)
190+
{
191+
Metadata.Tags.Add(tag);
192+
}
193+
}
194+
195+
void SyncObjectPacksToMetadata()
196+
{
197+
Metadata.ObjectPacks.Clear();
198+
foreach (var pack in ObjectPacks)
199+
{
200+
Metadata.ObjectPacks.Add(pack);
201+
}
202+
}
32203

33204
string? description;
34205
public string? Description
@@ -41,14 +212,14 @@ public string? Description
41212
}
42213
}
43214

44-
ObjectAvailability availability;
45-
public ObjectAvailability Availability
215+
DtoLicenceEntry? selectedLicence;
216+
public DtoLicenceEntry? SelectedLicence
46217
{
47-
get => availability;
218+
get => selectedLicence;
48219
set
49220
{
50-
_ = this.RaiseAndSetIfChanged(ref availability, value);
51-
Metadata.Availability = value;
221+
_ = this.RaiseAndSetIfChanged(ref selectedLicence, value);
222+
Metadata.Licence = value;
52223
}
53224
}
54225

0 commit comments

Comments
 (0)