summaryrefslogtreecommitdiff
path: root/Pages/Component
diff options
context:
space:
mode:
authorJake Mannens <jake@asger.xyz>2026-05-25 01:06:59 +1000
committerJake Mannens <jake@asger.xyz>2026-06-11 01:13:32 +1000
commita932b1bcb946bd70a5eb3e8fe1857fb74210a76b (patch)
treec3377cb963026e68bd29e34a21a6d319f378dee7 /Pages/Component
parent96d44f2947fd3ebef78411f65b09bbd2110d215f (diff)
Re-implemented existing features via the API
Diffstat (limited to 'Pages/Component')
-rw-r--r--Pages/Component/AboutDialog.razor44
-rw-r--r--Pages/Component/MediaTagTable.razor158
-rw-r--r--Pages/Component/TagSelectDialog.razor131
-rw-r--r--Pages/Component/Titlebar.razor7
4 files changed, 194 insertions, 146 deletions
diff --git a/Pages/Component/AboutDialog.razor b/Pages/Component/AboutDialog.razor
index fa7b1ca..888040e 100644
--- a/Pages/Component/AboutDialog.razor
+++ b/Pages/Component/AboutDialog.razor
@@ -1,9 +1,7 @@
-@using System.Reflection
-@*
- * @using Microsoft.AspNetCore.Hosting
- * @inject IDbContextFactory<HBContext> dbFactory
- * @inject IHostingEnvironment hostingEnvironment
- *@
+@using Microsoft.AspNetCore.Components.WebAssembly.Hosting
+@using System.Reflection
+@inject IWebAssemblyHostEnvironment hostEnvironment
+@inject HBSession hb
@implements IDialog
<Dialog @ref=dialog>
@@ -676,10 +674,11 @@
</div>
<a href="https://gitlab.com/plasmicplexus/HyperBooru-Server" target="_blank">Source</a>
<div id="progressContainer">
- @if(progress.HasValue) {
- var untagged = progress.Value.Untagged.ToString("N0");
- var total = progress.Value.Total.ToString("N0");
- var percent = (progress.Value.Untagged * 100f / progress.Value.Total).ToString("f1");
+ @if(ingestStatistics is not null) {
+ var untagged = ingestStatistics.UntaggedMediaCount.ToString("N0");
+ var total = ingestStatistics.TotalMediaCount.ToString("N0");
+ var percent =
+ (ingestStatistics.UntaggedMediaCount * 100f / ingestStatistics.TotalMediaCount).ToString("f1");
<p id="progress">Untagged: @($"{untagged}/{total} ({percent}%)")</p>
}
<ProgressBar @ref=progressBar />
@@ -694,23 +693,15 @@
private ProgressBar progressBar;
- private (long Untagged, long Total)? progress;
+ private IngestStatistics? ingestStatistics;
public bool Visible {
get => dialog.Visible;
set {
dialog.Visible = value;
- if(value) {
- // using var db = dbFactory.CreateDbContext();
- // progress = (
- // Untagged: db.Media
- // .Where(m => m.Tags.Any(t => t.TagDefinition.ObjectId == (int) HBObjectId.IngestTag))
- // .Count(),
- // Total: db.Media.Count()
- // );
- // progressBar.Progress = (float) progress.Value!.Untagged / (float) progress.Value!.Total;
- // InvokeAsync(() => StateHasChanged());
- }
+ InvokeAsync(() => StateHasChanged());
+ if(value)
+ LoadProgressAsync();
}
}
@@ -731,8 +722,15 @@
#if DEBUG
return "(Development)";
#else
- return hostingEnvironment.IsDevelopment() ? "(Development)" : null;
+ return hostEnvironment.IsDevelopment() ? "(Development)" : null;
#endif
}
}
+
+ private async void LoadProgressAsync() {
+ ingestStatistics = await hb.Statistics.GetIngestStatisticsAsync();
+ progressBar.Progress =
+ (float) ingestStatistics.UntaggedMediaCount / (float) ingestStatistics.TotalMediaCount;
+ await InvokeAsync(() => StateHasChanged());
+ }
}
diff --git a/Pages/Component/MediaTagTable.razor b/Pages/Component/MediaTagTable.razor
index 0524739..5e60d20 100644
--- a/Pages/Component/MediaTagTable.razor
+++ b/Pages/Component/MediaTagTable.razor
@@ -1,75 +1,109 @@
@inject HBSession hb
-<table class="data-table">
- <tr>
- <th>Namespace</th>
- <th>Tag Name</th>
- <th></th>
- </tr>
- @foreach(var e in tagDefs) {
- <tr>
- <td>
- @if(e.isImplicit) {
- <i>@e.tagDef.Namespace</i>
- } else {
- @e.tagDef.Namespace
- }
- </td>
- <td>
- <a href="/Gallery?t=@(e.tagDef.TagDefinitionId)" class="nondecorated">
- @if(e.isImplicit) {
- <i>@e.tagDef.Name</i>
- } else {
- @e.tagDef.Name
- }
- </a>
- </td>
- <td>
- @if(!e.isImplicit) {
- <a href="javascript:;" @onclick=@(() => Delete(e.tagDef))>Delete</a>
- } else {
- <a href="javascript:;" @onclick=@(() => MakeExplicit(e.tagDef))>Make Explicit</a>
- }
- </td>
- </tr>
- }
-</table>
+<LoadableContent T="List<TagTableEntry>" DataSource=LoadTagDefs @ref=tagContent>
+ <LoadingState>
+ <p><i>Loading...</i></p>
+ </LoadingState>
+ <ErrorState>
+ <p><i>Unable to fetch tags for this item!</i></p>
+ </ErrorState>
+ <LoadedState>
+ <table class="data-table">
+ <tr>
+ <th>Namespace</th>
+ <th>Tag Name</th>
+ <th></th>
+ </tr>
+ @{
+ var tags = tagContent.Data.Where(td => td.TagDefinition.Source == TagSource.UserTag);
+ }
+ @foreach(var e in tags) {
+ <tr>
+ <td>
+ @if(e.IsImplicit) {
+ <i>@e.TagDefinition.Namespace</i>
+ } else {
+ @e.TagDefinition.Namespace
+ }
+ </td>
+ <td>
+ <a href="/Gallery?t=@(e.TagDefinition.TagDefinitionId)" class="nondecorated">
+ @if(e.IsImplicit) {
+ <i>@e.TagDefinition.Name</i>
+ } else {
+ @e.TagDefinition.Name
+ }
+ </a>
+ </td>
+ <td>
+ @if(!e.IsImplicit) {
+ <a href="javascript:;" @onclick=@(() => Delete(e.TagDefinition))>Delete</a>
+ } else {
+ <a href="javascript:;" @onclick=@(() => MakeExplicit(e.TagDefinition))>Make Explicit</a>
+ }
+ </td>
+ </tr>
+ }
+ </table>
+ </LoadedState>
+</LoadableContent>
@code {
- [Parameter]
- public Guid MediaId { get; set; }
+ [Parameter]
+ public Guid MediaId { get; set; }
- private (TagDefinition tagDef, bool isImplicit)[] tagDefs = [];
+ private LoadableContent<List<TagTableEntry>> tagContent;
- protected override void OnInitialized() => LoadTagDefs();
+ private void Delete(TagDefinition tagDef) {
+ // tagService.RemoveTag(Media.Guid, tagDef.Guid);
+ // Refresh();
+ }
- public void Refresh() {
- LoadTagDefs();
- // StateHasChanged();
- }
+ private async Task<List<TagTableEntry>> LoadTagDefs() {
+ var itemTags = await hb.Media.GetTagsAsync(MediaId);
+ var allTags = await hb.Tag.GetTagDefinitionAsync();
- private void Delete(TagDefinition tagDef) {
- // tagService.RemoveTag(Media.Guid, tagDef.Guid);
- // Refresh();
- }
+ var tags = itemTags
+ .Select(td => new TagTableEntry() {
+ TagDefinition = td,
+ IsImplicit = false
+ })
+ .ToList();
- private async void LoadTagDefs() {
- var tags = await hb.Media.GetTagsAsync(MediaId);
+ while(true) {
+ var toAdd = allTags
+ .IntersectBy(tags
+ .SelectMany(t => t.TagDefinition.ImplicitTags), td => td.TagDefinitionId)
+ .Where(td => !tags
+ .Select(t => t.TagDefinition.TagDefinitionId)
+ .Contains(td.TagDefinitionId));
+ if(toAdd.Count() == 0)
+ break;
+ tags.AddRange(toAdd.Select(td => new TagTableEntry() {
+ TagDefinition = td,
+ IsImplicit = true
+ }));
+ }
- tagDefs = tags.Select(td => (tagDef: td, isImplicit: false)).ToArray();
+ return tags
+ .OrderBy(td => td.IsImplicit)
+ .ThenBy(td => td.TagDefinition.Namespace)
+ .ThenBy(td => td.TagDefinition.Name)
+ .ToList();
+ }
- await InvokeAsync(() => StateHasChanged());
+ private async Task MakeExplicit(TagDefinition tagDef) {
+ // TODO: Add error handling
+ await hb.Media.AddTagsAsync(MediaId, [ tagDef.TagDefinitionId ]);
+ //var index =
+ // tagDefs.IndexOf(tagDefs.First(td => td.tagDef.TagDefinitionId == tagDef.TagDefinitionId));
+ //tagDefs[index].isImplicit = false;
+ tagContent.Load();
+ await InvokeAsync(() => StateHasChanged());
+ }
- // using var db = dbFactory.CreateDbContext();
- // var media = db.Media.First(m => m.ObjectId == Media.ObjectId);
-
- // tagDefs = tagService.GetAllTags(Media)
- // .Where(e => e.tagDefinition.Source == TagSource.UserTag)
- // .ToArray();
- }
-
- private void MakeExplicit(TagDefinition tagDef) {
- // tagService.AddTag(Media, tagDef);
- // Refresh();
- }
+ private record TagTableEntry {
+ internal required TagDefinition TagDefinition { get; set; }
+ internal required bool IsImplicit { get; set; }
+ }
}
diff --git a/Pages/Component/TagSelectDialog.razor b/Pages/Component/TagSelectDialog.razor
index 99321fe..5872ce7 100644
--- a/Pages/Component/TagSelectDialog.razor
+++ b/Pages/Component/TagSelectDialog.razor
@@ -3,49 +3,57 @@
* @inject ITagService tagService
* @inject IUserService userService
*@
+@inject HBSession hb
@implements IDisposable
@implements IDialog
-<link rel="stylesheet" href="@(nameof(HyperBooru)).styles.css"/>
-
<Dialog Title=@(Title ?? "Select one or more tag(s)") @ref=dialog>
- <input
- type="text"
- placeholder="Search"
- autocorrect="off"
- autocapitalize="off"
- autocomplete="off"
- @ref=queryInput
- @oninput=QueryInput
- @onkeypress=QueryKey
- value=@query/>
- <div class="tag-definitions">
- @for(int i = 0; i < tagDefinitions.Count(); i++) {
- if(!MatchesQuery(tagDefinitions[i].tagDefinition))
- continue;
- var local = i;
- var ns = tagDefinitions[i].tagDefinition.Namespace;
- var alias = tagDefinitions[i].tagDefinition.Alias;
- var title = string.Join(" ", new[] {
- ns,
- alias is not null ? $"({alias})" : null
- });
-@*
- <input
- type="checkbox"
- id="tagDef-@tagDefinitions[i].tagDefinition.Guid"
- @bind=tagDefinitions[local].selected />
- <label
- for="tagDef-@tagDefinitions[i].tagDefinition.Guid"
- title=@title>
- @tagDefinitions[i].tagDefinition.Name
- </label>
-*@
- }
- </div>
+ @switch(state) {
+ case ComponentState.Loading:
+ <p><i>Loading...</i></p>
+ break;
+ case ComponentState.Error:
+ break;
+ case ComponentState.Loaded:
+ <input
+ type="text"
+ placeholder="Search"
+ autocorrect="off"
+ autocapitalize="off"
+ autocomplete="off"
+ @ref=queryInput
+ @oninput=QueryInput
+ @onkeypress=QueryKey
+ value=@query/>
+ <div class="tag-definitions">
+ @for(int i = 0; i < tagDefinitions.Count(); i++) {
+ if(!MatchesQuery(tagDefinitions[i].tagDefinition))
+ continue;
+ var local = i;
+ var ns = tagDefinitions[i].tagDefinition.Namespace;
+ var alias = tagDefinitions[i].tagDefinition.Alias;
+ var title = string.Join(" ", new[] {
+ ns,
+ alias is not null ? $"({alias})" : null
+ });
+ <input
+ type="checkbox"
+ id="tagDef-@tagDefinitions[i].tagDefinition.TagDefinitionId"
+ @bind=tagDefinitions[local].selected />
+ <label
+ for="tagDef-@tagDefinitions[i].tagDefinition.TagDefinitionId"
+ title=@title>
+ @tagDefinitions[i].tagDefinition.Name
+ </label>
+ }
+ </div>
+ break;
+ }
<ButtonContainer>
<button @onclick=@(() => dialog.Hide()) class="secondary">Cancel</button>
- <button @onclick=@(() => Submit())>Accept</button>
+ @if(state == ComponentState.Loaded) {
+ <button @onclick=@(() => Submit())>Accept</button>
+ }
</ButtonContainer>
</Dialog>
@@ -57,19 +65,21 @@
public EventCallback<TagDefinition[]> OnSubmit { get; set; }
public TagDefinition[] SelectedTags { get; set; } =
- Array.Empty<TagDefinition>();
+ Array.Empty<TagDefinition>();
public bool Visible {
get => visible;
set {
if(value)
- LoadTags();
+ LoadTagsAsync();
query = null;
visible = dialog.Visible = value;
}
}
- private (TagDefinition tagDefinition, bool selected)[] tagDefinitions;
+ private (TagDefinition tagDefinition, bool selected)[] tagDefinitions = [];
+
+ private ComponentState state = ComponentState.Loading;
// private HBContext db;
@@ -85,32 +95,39 @@
protected override void OnInitialized() {
// userService.UserSessionState.OnStateChange += ShowNsfwChanged;
- LoadTags();
+ LoadTagsAsync();
}
- private void LoadTags() {
+ private async void LoadTagsAsync() {
// db = dbFactory.CreateDbContext();
- // var selected = SelectedTags.Select(td => td.Guid);
+ var selected = SelectedTags.Select(td => td.TagDefinitionId);
+
+ // TODO: Factor in whether show NSFW is actually selected
+ bool showNsfw = true;
- // int[] nsfwTags = Array.Empty<int>();
+ Guid[] nsfwTags = Array.Empty<Guid>();
// if(!userService.UserSessionState.ShowNsfw)
// nsfwTags = tagService.TagsThatImply(HBContext.NsfwTag)
// .Select(td => td.ObjectId)
// .ToArray();
- // tagDefinitions = db.TagDefinitions
- // .Include(td => td.ImplicitTags)
- // .Where(td => td.Source == TagSource.UserTag)
- // .OrderBy(td => td.Name)
- // .AsEnumerable()
- // .Where(td => userService.UserSessionState.ShowNsfw || !td.ImplicitTags
- // .IntersectBy(nsfwTags, td => td.ObjectId)
- // .Any())
- // .Select(td => new Tuple<TagDefinition, bool>(
- // td,
- // selected.Contains(td.Guid)).ToValueTuple())
- // .ToArray();
+ try {
+ tagDefinitions = (await hb.Tag.GetTagDefinitionAsync())
+ // .Where(td => td.Source == TagSource.UserTag)
+ .OrderBy(td => td.Name)
+ .Where(td => showNsfw || !td.ImplicitTags.Intersect(nsfwTags).Any())
+ .Select(td => new Tuple<TagDefinition, bool>(
+ td,
+ selected.Contains(td.TagDefinitionId)).ToValueTuple())
+ .ToArray();
+
+ state = ComponentState.Loaded;
+ } catch {
+ state = ComponentState.Error;
+ }
+
+ await InvokeAsync(() => StateHasChanged());
}
private void QueryInput(ChangeEventArgs e) {
@@ -169,7 +186,7 @@
// return tagDef.Guid == singleTag.Guid;
// return tagDef.Name.ToLower().Contains(query.ToLower());
- return false;
+ return true;
}
private async void Submit() {
diff --git a/Pages/Component/Titlebar.razor b/Pages/Component/Titlebar.razor
index 521fb46..65af89a 100644
--- a/Pages/Component/Titlebar.razor
+++ b/Pages/Component/Titlebar.razor
@@ -51,8 +51,7 @@
<a class="desktop" href="/TagDefinitions">Tags</a>
<a class="desktop" href="/Gallery?ingest=true">Ingest</a>
<a class="desktop" href="/Upload">Upload</a>
- @* <a class="desktop" href="javascript:;" @onclick=@(() => aboutDialog.Show())>About</a> *@
- <a class="desktop" href="javascript:;">About</a>
+ <a class="desktop" href="javascript:;" @onclick=@(() => aboutDialog.Show())>About</a>
<p class="desktop" id="nsfw-label">NSFW</p>
<div id="nsfw-switch" class="desktop">
@@ -63,7 +62,7 @@
</form>
<a class="desktop" href="javascript:logout();">Logout</a>
</div>
- @* <AboutDialog @ref=aboutDialog/> *@
+ <AboutDialog @ref=aboutDialog/>
} else {
<div id="navbar">
<h2>Login</h2>
@@ -84,7 +83,7 @@
}
@code {
- // private AboutDialog aboutDialog;
+ private AboutDialog aboutDialog;
public string Username { get; set; } = "";
public string Password { get; set; } = "";