From f79643b43cbb5f673d5b9d043d3ee1bdd54af869 Mon Sep 17 00:00:00 2001 From: Jake Mannens Date: Mon, 25 May 2026 01:06:59 +1000 Subject: Re-implemented existing features via the API --- Pages/Component/AboutDialog.razor | 44 +++--- Pages/Component/MediaTagTable.razor | 158 ++++++++++++-------- Pages/Component/TagSelectDialog.razor | 234 +++++++++++++++--------------- Pages/Component/TagSelectDialog.razor.css | 6 +- Pages/Component/Titlebar.razor | 7 +- 5 files changed, 240 insertions(+), 209 deletions(-) (limited to 'Pages/Component') 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 dbFactory - * @inject IHostingEnvironment hostingEnvironment - *@ +@using Microsoft.AspNetCore.Components.WebAssembly.Hosting +@using System.Reflection +@inject IWebAssemblyHostEnvironment hostEnvironment +@inject HBSession hb @implements IDialog @@ -676,10 +674,11 @@ Source
- @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");

Untagged: @($"{untagged}/{total} ({percent}%)")

} @@ -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..14ddc6d 100644 --- a/Pages/Component/MediaTagTable.razor +++ b/Pages/Component/MediaTagTable.razor @@ -1,75 +1,111 @@ @inject HBSession hb - - - - - - - @foreach(var e in tagDefs) { - - - - - - } -
NamespaceTag Name
- @if(e.isImplicit) { - @e.tagDef.Namespace - } else { - @e.tagDef.Namespace - } - - - @if(e.isImplicit) { - @e.tagDef.Name - } else { - @e.tagDef.Name - } - - - @if(!e.isImplicit) { - Delete(e.tagDef))>Delete - } else { - MakeExplicit(e.tagDef))>Make Explicit - } -
+ + +

Loading...

+
+ +

Unable to fetch tags for this item!

+
+ + + + + + + + @{ + var tags = tagContent.Data.Where(td => td.TagDefinition.Source == TagSource.UserTag); + } + @foreach(var e in tags) { + + + + + + } +
NamespaceTag Name
+ @if(e.IsImplicit) { + @e.TagDefinition.Namespace + } else { + @e.TagDefinition.Namespace + } + + + @if(e.IsImplicit) { + @e.TagDefinition.Name + } else { + @e.TagDefinition.Name + } + + + @if(!e.IsImplicit) { + Delete(e.TagDefinition))>Delete + } else { + MakeExplicit(e.TagDefinition))>Make Explicit + } +
+
+
@code { - [Parameter] - public Guid MediaId { get; set; } + [Parameter] + public Guid MediaId { get; set; } - private (TagDefinition tagDef, bool isImplicit)[] tagDefs = []; + public List Data => tagContent.Data; - protected override void OnInitialized() => LoadTagDefs(); + private LoadableContent> tagContent; - public void Refresh() { - LoadTagDefs(); - // StateHasChanged(); - } + private void Delete(TagDefinition tagDef) { + // tagService.RemoveTag(Media.Guid, tagDef.Guid); + // Refresh(); + } - private void Delete(TagDefinition tagDef) { - // tagService.RemoveTag(Media.Guid, tagDef.Guid); - // Refresh(); - } + private async Task> LoadTagDefs() { + var itemTags = await hb.Media.GetTagsAsync(MediaId); + var allTags = await hb.Tag.GetTagDefinitionAsync(); - private async void LoadTagDefs() { - var tags = await hb.Media.GetTagsAsync(MediaId); + var tags = itemTags + .Select(td => new TagTableEntry() { + TagDefinition = td, + IsImplicit = false + }) + .ToList(); - tagDefs = tags.Select(td => (tagDef: td, isImplicit: false)).ToArray(); + 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 + })); + } - await InvokeAsync(() => StateHasChanged()); + return tags + .OrderBy(td => td.IsImplicit) + .ThenBy(td => td.TagDefinition.Namespace) + .ThenBy(td => td.TagDefinition.Name) + .ToList(); + } - // using var db = dbFactory.CreateDbContext(); - // var media = db.Media.First(m => m.ObjectId == Media.ObjectId); + 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()); + } - // tagDefs = tagService.GetAllTags(Media) - // .Where(e => e.tagDefinition.Source == TagSource.UserTag) - // .ToArray(); - } - - private void MakeExplicit(TagDefinition tagDef) { - // tagService.AddTag(Media, tagDef); - // Refresh(); - } + public record TagTableEntry { + public required TagDefinition TagDefinition { get; set; } + public required bool IsImplicit { get; set; } + } } diff --git a/Pages/Component/TagSelectDialog.razor b/Pages/Component/TagSelectDialog.razor index 99321fe..003a3b6 100644 --- a/Pages/Component/TagSelectDialog.razor +++ b/Pages/Component/TagSelectDialog.razor @@ -1,51 +1,56 @@ -@* - * @inject IDbContextFactory dbFactory - * @inject ITagService tagService - * @inject IUserService userService - *@ -@implements IDisposable +@inject HBSession hb @implements IDialog - - - -
- @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 - }); -@* - - -*@ - } -
+ + + + + + + +

Error loading tags...

+
+ + +
+ @foreach(var td in MatchesQuery) { + var ns = td.TagDefinition.Namespace; + var alias = td.TagDefinition.Alias; + var title = string.Join(" ", new[] { + ns, + alias is not null ? $"({alias})" : null + }); + + + } +
+
+
- + @if(state == ComponentState.Loaded) { + + }
@@ -57,21 +62,54 @@ public EventCallback OnSubmit { get; set; } public TagDefinition[] SelectedTags { get; set; } = - Array.Empty(); + Array.Empty(); public bool Visible { get => visible; set { - if(value) - LoadTags(); + if(value && tagDefinitions.Data is not null) + foreach(var td in tagDefinitions.Data) + td.Selected = false; query = null; visible = dialog.Visible = value; + + InvokeAsync(() => StateHasChanged()); } } - private (TagDefinition tagDefinition, bool selected)[] tagDefinitions; + private IEnumerable MatchesQuery { + get { + if(string.IsNullOrEmpty(query)) + return tagDefinitions.Data; + + var matchesAlias = tagDefinitions.Data + .FirstOrDefault(td => string.Equals( + td.TagDefinition.Alias, + query, + StringComparison.OrdinalIgnoreCase)); + var matchesName = tagDefinitions.Data + .FirstOrDefault(td => string.Equals( + td.TagDefinition.Name, + query, + StringComparison.OrdinalIgnoreCase)); + + if((matchesAlias ?? matchesName) is not null) { + return [ (matchesAlias ?? matchesName!) ]; + } else { + return tagDefinitions.Data.Where(td => { + if(string.Equals(td.TagDefinition.Alias, query, StringComparison.OrdinalIgnoreCase)) + return true; + if(string.Equals(td.TagDefinition.Name, query, StringComparison.OrdinalIgnoreCase)) + return true; + if(td.TagDefinition.Name.ToLower().Contains(query!.ToLower())) + return true; + return false; + }); + } + } + } - // private HBContext db; + private LoadableContent> tagDefinitions; private Dialog dialog; @@ -83,39 +121,33 @@ public void Show() => Visible = true; public void Hide() => Visible = false; - protected override void OnInitialized() { - // userService.UserSessionState.OnStateChange += ShowNsfwChanged; - LoadTags(); - } - - private void LoadTags() { - // db = dbFactory.CreateDbContext(); + private async Task> LoadTagsAsync() { + var selected = SelectedTags.Select(td => td.TagDefinitionId); - // var selected = SelectedTags.Select(td => td.Guid); + // TODO: Factor in whether show NSFW is actually selected + bool showNsfw = true; - // int[] nsfwTags = Array.Empty(); + Guid[] nsfwTags = Array.Empty(); // 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( - // td, - // selected.Contains(td.Guid)).ToValueTuple()) - // .ToArray(); + return (await hb.Tag.GetTagDefinitionAsync()) + .Where(td => td.Source == TagSource.UserTag) + .OrderBy(td => td.Name) + .Where(td => td.Source == TagSource.UserTag) + .Where(td => showNsfw || !td.ImplicitTags.Intersect(nsfwTags).Any()) + .Select(td => new SelectableTag() { + TagDefinition = td, + Selected = selected.Contains(td.TagDefinitionId) + }) + .ToList(); } private void QueryInput(ChangeEventArgs e) { query = (string?) e.Value; - StateHasChanged(); + InvokeAsync(() => StateHasChanged()); } private void QueryKey(KeyboardEventArgs e) { @@ -127,61 +159,23 @@ return; } - int c = 0; - int? last = null; - for(int i = 0; i < tagDefinitions.Count(); i++) { - if(!MatchesQuery(tagDefinitions[i].tagDefinition)) - continue; - last = i; - c++; - } - - if(c == 1 && last is not null) - tagDefinitions[(int) last].selected = - !tagDefinitions[(int) last].selected; + if(MatchesQuery.Count() == 1) + MatchesQuery.First().Selected ^= true; query = null; - StateHasChanged(); - } - - private bool MatchesQuery(TagDefinition tagDef) { - // TagDefinition? singleTag = null; - - // if(string.IsNullOrEmpty(query)) - // return true; - - // singleTag = tagDefinitions.FirstOrDefault( - // e => string.Equals( - // e.tagDefinition.Alias, - // query, - // StringComparison.OrdinalIgnoreCase)).tagDefinition; - - // if(singleTag is not null) - // return tagDef.Guid == singleTag.Guid; - - // singleTag = tagDefinitions.FirstOrDefault( - // e => string.Equals( - // e.tagDefinition.Name, - // query, - // StringComparison.OrdinalIgnoreCase)).tagDefinition; - - // if(singleTag is not null) - // return tagDef.Guid == singleTag.Guid; - - // return tagDef.Name.ToLower().Contains(query.ToLower()); - return false; + InvokeAsync(() => StateHasChanged()); } private async void Submit() { await OnSubmit.InvokeAsync( - tagDefinitions - .Where(e => e.selected) - .Select(e => e.tagDefinition) + tagDefinitions.Data + .Where(td => td.Selected) + .Select(td => td.TagDefinition) .ToArray()); - for(int i = 0; i < tagDefinitions.Count(); i++) - tagDefinitions[i].selected = false; + foreach(var td in tagDefinitions.Data) + td.Selected = false; Hide(); - StateHasChanged(); + await InvokeAsync(() => StateHasChanged()); } // public async void ShowNsfwChanged(UserSessionState userSessionState) => @@ -190,8 +184,8 @@ // StateHasChanged(); // }); - public void Dispose() { - // db.Dispose(); - // userService.UserSessionState.OnStateChange -= ShowNsfwChanged; + private record SelectableTag { + public required TagDefinition TagDefinition { get; set; } + public bool Selected { get; set; } = false; } } diff --git a/Pages/Component/TagSelectDialog.razor.css b/Pages/Component/TagSelectDialog.razor.css index dadd0c4..3c8d92a 100644 --- a/Pages/Component/TagSelectDialog.razor.css +++ b/Pages/Component/TagSelectDialog.razor.css @@ -1,4 +1,8 @@ -div.tag-definitions { +p { + text-align: center; +} + +div.tag-definitions { max-height: 450px; overflow-y: auto; user-select: none; 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 @@ Tags Ingest Upload - @* aboutDialog.Show())>About *@ - About + aboutDialog.Show())>About

NSFW

@@ -63,7 +62,7 @@ Logout
- @* *@ + } else {