summaryrefslogtreecommitdiff
path: root/Pages
diff options
context:
space:
mode:
authorJake Mannens <jake@asger.xyz>2026-05-25 01:06:59 +1000
committerJake Mannens <jake@asger.xyz>2026-06-17 02:42:47 +1000
commitf79643b43cbb5f673d5b9d043d3ee1bdd54af869 (patch)
treed3622f5996acc5407f27d28376c8c8ab0d3a58e1 /Pages
parent308b0b03ed0875d8cc10ab15586ff12d0ea6ddf8 (diff)
Re-implemented existing features via the API
Diffstat (limited to 'Pages')
-rw-r--r--Pages/Component/AboutDialog.razor44
-rw-r--r--Pages/Component/MediaTagTable.razor158
-rw-r--r--Pages/Component/TagSelectDialog.razor234
-rw-r--r--Pages/Component/TagSelectDialog.razor.css6
-rw-r--r--Pages/Component/Titlebar.razor7
-rw-r--r--Pages/TagDefinitions.razor77
-rw-r--r--Pages/ViewMedia.razor211
7 files changed, 400 insertions, 337 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..14ddc6d 100644
--- a/Pages/Component/MediaTagTable.razor
+++ b/Pages/Component/MediaTagTable.razor
@@ -1,75 +1,111 @@
@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 = [];
+ public List<TagTableEntry> Data => tagContent.Data;
- protected override void OnInitialized() => LoadTagDefs();
+ private LoadableContent<List<TagTableEntry>> 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<List<TagTableEntry>> 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<HBContext> dbFactory
- * @inject ITagService tagService
- * @inject IUserService userService
- *@
-@implements IDisposable
+@inject HBSession hb
@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>
+ <LoadableContent
+ T="List<SelectableTag>"
+ DataSource=LoadTagsAsync
+ @ref=tagDefinitions>
+
+ <LoadingState>
+ <LoadingSpinner/>
+ </LoadingState>
+ <ErrorState>
+ <ErrorIcon Size=60/>
+ <p><i>Error loading tags...</i></p>
+ </ErrorState>
+ <LoadedState>
+ <input
+ type="text"
+ placeholder="Search"
+ autocorrect="off"
+ autocapitalize="off"
+ autocomplete="off"
+ @ref=queryInput
+ @oninput=QueryInput
+ @onkeypress=QueryKey
+ value=@query/>
+ <div class="tag-definitions">
+ @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
+ });
+ <input
+ type="checkbox"
+ id="tagDef-@td.TagDefinition.TagDefinitionId"
+ @bind=@td.Selected />
+ <label
+ for="tagDef-@td.TagDefinition.TagDefinitionId"
+ title=@title>
+ @td.TagDefinition.Name
+ </label>
+ }
+ </div>
+ </LoadedState>
+ </LoadableContent>
<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,21 +62,54 @@
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();
+ 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<SelectableTag> MatchesQuery {
+ get {
+ if(string.IsNullOrEmpty(query))
+ return tagDefinitions.Data;
- // private HBContext db;
+ 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 LoadableContent<List<SelectableTag>> 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<List<SelectableTag>> 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<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();
+ 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 @@
<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; } = "";
diff --git a/Pages/TagDefinitions.razor b/Pages/TagDefinitions.razor
index 7ce5400..b9f90d6 100644
--- a/Pages/TagDefinitions.razor
+++ b/Pages/TagDefinitions.razor
@@ -5,6 +5,7 @@
* @inject IUserService userService
* @implements IDisposable
*@
+@inject HBSession hb
<PageTitle>Tag Definitions</PageTitle>
@@ -24,28 +25,27 @@
<th></th>
</tr>
@foreach(var tagDef in tagDefinitions.Where(td => td.Namespace == ns)) {
- @* <tr data-guid="@tagDef.Guid"> *@
- <tr>
+ <tr data-guid="@tagDef.TagDefinitionId">
<td>@tagDef.Alias</td>
<td>
-@*
- <a href="/Gallery?t=@tagDef.Guid" class="nondecorated">
+ <a href="/Gallery?t=@tagDef.TagDefinitionId" class="nondecorated">
@tagDef.Name
</a>
-*@
</td>
<td>
<i>
@{
- // var implicitTags = tagDef.ImplicitTags
- // .Where(td => td.Source == TagSource.UserTag);
- // foreach(var tag in implicitTags) {
- // <a href="/Gallery?t=@tag.Guid" class="nondecorated">
- // @tag.Name
- // </a>
- // if(tag != implicitTags.Last())
- // @(", ")
- // }
+ var implicitTags = tagDefinitions
+ .IntersectBy(tagDef.ImplicitTags, td => td.TagDefinitionId)
+ .OrderBy(td => td.Name);
+ // .Where(td => td.Source == TagSource.UserTag);
+ foreach(var tag in implicitTags) {
+ <a href="/Gallery?t=@tag.TagDefinitionId" class="nondecorated">
+ @tag.Name
+ </a>
+ if(tag != implicitTags.Last())
+ @(", ")
+ }
}
</i>
</td>
@@ -97,37 +97,38 @@
private TagDefinition[] tagDefinitions;
- private string?[] tagNamespaces;
+ private string[] tagNamespaces = Array.Empty<string>();
// protected override void OnInitialized() =>
// userService.UserSessionState.OnStateChange += ShowNsfwChanged;
protected override void OnParametersSet() =>
- LoadTags();
+ LoadTagsAsync();
+
+ private async void LoadTagsAsync() {
+ bool showNsfw = true;
- private void LoadTags() {
- // 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 = dbFactory.CreateDbContext().TagDefinitions
- // .Include(td => td.ImplicitTags)
- // .Where(td => td.Source == TagSource.UserTag)
- // .OrderBy(td => td.Namespace)
- // .ThenBy(td => td.Name)
- // .AsEnumerable()
- // .Where(td => userService.UserSessionState.ShowNsfw || !td.ImplicitTags
- // .IntersectBy(nsfwTags, td => td.ObjectId)
- // .Any())
- // .ToArray();
+ tagDefinitions = (await hb.Tag.GetTagDefinitionAsync())
+ // TODO: Limit shown tags to user-tags
+ //.Where(td => td.Source == TagSource.UserTag)
+ .OrderBy(td => td.Namespace)
+ .ThenBy(td => td.Name)
+ .Where(td => showNsfw || !td.ImplicitTags.Intersect(nsfwTags).Any())
+ .ToArray();
+
+ tagNamespaces = tagDefinitions
+ .Select(td => td.Namespace)
+ .Order()
+ .Distinct()
+ .ToArray();
- // tagNamespaces = tagDefinitions
- // .Select(td => td.Namespace)
- // .Order()
- // .Distinct()
- // .ToArray();
+ await InvokeAsync(() => StateHasChanged());
}
private void PromptToDelete(TagDefinition toDelete) {
@@ -141,7 +142,7 @@
// tagService.DeleteTagDefinition(toDelete);
// deleteTagDialog.Hide();
- // LoadTags();
+ // LoadTagsAsync();
// StateHasChanged();
}
@@ -153,7 +154,7 @@
}
private void TagUpdated(object? sender, EventArgs e) {
- LoadTags();
+ LoadTagsAsync();
StateHasChanged();
}
@@ -169,7 +170,7 @@
return;
// tagService.SetImplicitTags(toEditImplicit, tagDefs);
- LoadTags();
+ LoadTagsAsync();
StateHasChanged();
}
@@ -178,13 +179,13 @@
// tagService.AddImplicitTag(tagDef.Guid, HBContext.NsfwTag);
// else
// tagService.RemoveImplicitTag(tagDef.Guid, HBContext.NsfwTag);
- // LoadTags();
+ // LoadTagsAsync();
// StateHasChanged();
}
// private async void ShowNsfwChanged(UserSessionState userSessionState) =>
// await InvokeAsync(() => {
- // LoadTags();
+ // LoadTagsAsync();
// StateHasChanged();
// });
diff --git a/Pages/ViewMedia.razor b/Pages/ViewMedia.razor
index e210e79..73ded5f 100644
--- a/Pages/ViewMedia.razor
+++ b/Pages/ViewMedia.razor
@@ -1,9 +1,9 @@
@page "/ViewMedia"
-@* @using HyperBooru.Util*@
+@inject ISourceService sourceService
@inject HBSession hb
@inject IJSRuntime jsRuntime
-<PageTitle>@title</PageTitle>
+<PageTitle>@Title</PageTitle>
<script suppress-warning="BL9992">
function toggleSidebar() {
@@ -44,7 +44,6 @@
<div id="metadata-container">
<div id="metadata-fileinfo">
@if(infoEditMode) {
-@*
<form action="javascript:;" @onsubmit=@(() => ApplyInfoEdit(true))>
<table id="edit-metadata">
<tr>
@@ -57,13 +56,11 @@
</tr>
</table>
</form>
-*@
} else {
<p>Title: <i>@(media?.ShortDescription ?? "None")</i></p>
<p class="newlines">Description:<br/><i>@(media?.LongDescription ?? "None")</i></p>
}
-@*
- <p>Resolution: @(media.CurrentUploadedFile.Width)x@(media.CurrentUploadedFile.Height)</p>
+ @*<p>Resolution: @(media.CurrentUploadedFile.Width)x@(media.CurrentUploadedFile.Height)</p>*@
<p class="heading">Upload history</p>
<hr/>
<table id="uploaded-files" class="data-table">
@@ -75,36 +72,45 @@
<th>Size</th>
<th>Original Checksum</th>
</tr>
- @foreach(var file in media.UploadedFiles.OrderByDescending(uf => uf.UploadTime)) {
- string? sourceUrl = null;
- if(file.Filename is not null)
- sourceUrl = sourceService.GetUrlFromFilename(file.Filename);
- <tr>
- <td title=@file.CreateTime?.ToString()>
- @(file.CreateTime?.ToString("d") ?? "N/A")
- </td>
- <td title=@file.LastWriteTime?.ToString()>
- @(file.LastWriteTime?.ToString("d") ?? "N/A")
- </td>
- <td title=@file.UploadTime>@(file.UploadTime.ToString("d"))</td>
- <td title=@(file.Path is not null ? $"{file.Path.Replace('\\', '/')}/{file.Filename}" : file.Filename)>
- @if(sourceUrl is not null) {
- <a class="nondecorated" target="_blank" href=@sourceUrl>@file.Filename</a>
- } else {
- @file.Filename
- }
- </td>
- <td title=@file.Length>@file.Length.ToBytesSI()</td>
- <td
- title=@(file.Checksum + (file.ChecksumVerified ? " (verified)" : ""))
- class=@(file.ChecksumVerified ? "verified" : null)>
-
- @file.Checksum.Substring(0, 8)
- </td>
- </tr>
- }
+ <LoadableContent T="List<UploadedFile>" DataSource=LoadUploadedFiles @ref=uploadedFilesContent>
+ <LoadingState>
+ <p><i>Loading...</i></p>
+ </LoadingState>
+ <ErrorState>
+ <p><i>Unable to fetch file info for this item!</i></p>
+ </ErrorState>
+ <LoadedState>
+ @foreach(var file in uploadedFilesContent.Data!) {
+ string? sourceUrl = null;
+ if(file.Filename is not null)
+ sourceUrl = sourceService.GetUrlFromFilename(file.Filename);
+ <tr>
+ <td title=@file.CreateTime?.ToString()>
+ @(file.CreateTime?.ToString("d") ?? "N/A")
+ </td>
+ <td title=@file.LastWriteTime?.ToString()>
+ @(file.LastWriteTime?.ToString("d") ?? "N/A")
+ </td>
+ <td title=@file.UploadTime>@(file.UploadTime.ToString("d"))</td>
+ <td title=@(file.Path is not null ? $"{file.Path.Replace('\\', '/')}/{file.Filename}" : file.Filename)>
+ @if(sourceUrl is not null) {
+ <a class="nondecorated" target="_blank" href=@sourceUrl>@file.Filename</a>
+ } else {
+ @file.Filename
+ }
+ </td>
+ <td title=@file.Length>@file.Length.ToBytesSI()</td>
+ <td
+ title=@(file.Checksum + (file.ChecksumVerified ? " (verified)" : ""))
+ class=@(file.ChecksumVerified ? "verified" : null)>
+
+ @file.Checksum.Substring(0, 8)
+ </td>
+ </tr>
+ }
+ </LoadedState>
+ </LoadableContent>
</table>
-*@
</div>
<div id="metadata-tags">
<p class="heading">Tags</p>
@@ -114,8 +120,7 @@
</div>
<div id="button-container">
<ButtonContainer>
-@*
- <button @onclick=@(() => deleteDialog.Show()) class="warning" data-keyboard-shortcut="d">
+ <button @onclick=@(() => deleteDialog.Show()) class="warning" data-keyboard-shortcut="d">
<img src="/images/trash.svg"/>
<p><u>D</u>elete</p>
</button>
@@ -127,14 +132,13 @@
<img src="/images/book.svg"/>
<p>View <u>O</u>CR</p>
</button>
-*@
@if(infoEditMode) {
@*
- <button @onclick=@(() => ApplyInfoEdit(false)) class="secondary">
+ <button @onclick=@(() => await ApplyInfoEdit(false)) class="secondary">
<img src="/images/cross.svg"/>
<p>Cancel</p>
</button>
- <button @onclick=@(() => ApplyInfoEdit(true))>
+ <button @onclick=@(() => await ApplyInfoEdit(true))>
<img src="/images/checkmark.svg"/>
<p>Apply</p>
</button>
@@ -168,7 +172,6 @@
</div>
</div>
-@*
<Dialog Title="Delete this media?" @ref=deleteDialog>
<ButtonContainer>
<button @onclick=@(() => deleteDialog.Hide()) class="secondary">Cancel</button>
@@ -177,11 +180,13 @@
</Dialog>
<Dialog Title="OCR Data" @ref=ocrDialog>
- @if(media.OcrData is null) {
+ @*@if(media.OcrData is null) {*@
<p><center>This media item hasn't been scanned yet!</center></p>
+@*
} else {
<code style="max-height:400px;">@media.OcrData?.Text</code>
}
+*@
<ButtonContainer>
<button @onclick=@(() => ocrDialog.Hide())>Close</button>
</ButtonContainer>
@@ -191,14 +196,15 @@
Title="Select one or more tag(s) to add"
OnSubmit=AddTags
@ref=tagDialog/>
-*@
@code {
[Parameter]
[SupplyParameterFromQuery(Name = "m")]
public Guid MediaId { get; set; }
- private ApiModels.Media media;
+ private ApiModels.Media? media;
+
+ private LoadableContent<List<UploadedFile>> uploadedFilesContent;
private string title;
@@ -207,9 +213,9 @@
private string? longDescription;
private MediaTagTable mediaTagTable;
- // private Dialog deleteDialog;
- // private Dialog ocrDialog;
- // private TagSelectDialog tagDialog;
+ private Dialog deleteDialog;
+ private Dialog ocrDialog;
+ private TagSelectDialog tagDialog;
private ElementReference shortDescriptionInput;
@@ -223,56 +229,81 @@
private async void LoadMedia() {
media = await hb.Media.GetAsync(MediaId);
- // using var db = dbFactory.CreateDbContext();
- // media = db.Media
- // .Include(m => m.Tags)
- // .ThenInclude(t => t.TagDefinition)
- // .Include(m => m.CurrentUploadedFile)
- // .Include(m => m.UploadedFiles)
- // .Include(m => m.OcrData)
- // .First(m => m.Guid == MediaId);
+ await InvokeAsync(() => StateHasChanged());
+ }
+
+ private async Task<List<UploadedFile>> LoadUploadedFiles() {
+ return (await hb.Media.GetUploadedFilesAsync(MediaId))
+ .OrderByDescending(uf => uf.UploadTime)
+ .ToList();
+ }
- // title = media.DisplayName ?? "Media View";
- // InvokeAsync(() => StateHasChanged());
+ private void AddTags(TagDefinition[] tagDefs) {
+ // foreach(var tagDef in tagDefs)
+ // tagService.AddTag(media, tagDef);
+ // mediaTagTable.Refresh();
}
- // private void AddTags(TagDefinition[] tagDefs) {
- // foreach(var tagDef in tagDefs)
- // tagService.AddTag(media, tagDef);
- // mediaTagTable.Refresh();
- // }
+ private async void SetIngest(bool ingest) {
+ // mediaService.SetIngest(media, ingest);
+ LoadMedia();
- private async void SetIngest(bool ingest) {
- // mediaService.SetIngest(media, ingest);
- LoadMedia();
+ if(ingest)
+ StateHasChanged();
+ else
+ await jsRuntime.InvokeVoidAsync("history.back");
+ }
- if(ingest)
- StateHasChanged();
- else
- await jsRuntime.InvokeVoidAsync("history.back");
- }
+ private string Title {
+ get {
+ if(media is null)
+ return "View Media";
- private bool InfoEditMode {
- get => infoEditMode;
- set {
- shortDescription = media.ShortDescription;
- longDescription = media.LongDescription;
- infoEditMode = value;
- StateHasChanged();
- }
- }
+ if(media.ShortDescription is not null)
+ return media.ShortDescription;
- // private void ApplyInfoEdit(bool apply) {
- // if(apply) {
- // mediaService.SetDescription(media, shortDescription, longDescription);
- // LoadMedia();
- // }
+ if(uploadedFilesContent.Data is null)
+ return $"Media ({media.MediaId.ToString().ToUpper().Substring(0, 8)})";
+
+ return uploadedFilesContent.Data
+ .OrderBy(f => f.UploadTime)
+ .FirstOrDefault()?.Filename ?? media.MediaId.ToString().ToUpper();
+ }
+ }
+
+ private bool InfoEditMode {
+ get => infoEditMode;
+ set {
+ shortDescription = media.ShortDescription;
+ longDescription = media.LongDescription;
+ infoEditMode = value;
+ InvokeAsync(() => StateHasChanged());
+ }
+ }
- // infoEditMode = false;
- // }
+ private async Task ApplyInfoEdit(bool apply) {
+ if(apply) {
+ var updatedMedia = new ApiModels.Media() {
+ MediaId = MediaId,
+ ShortDescription = shortDescription?.NullIfEmpty(),
+ LongDescription = longDescription?.NullIfEmpty()
+ };
- // private async void DeleteMedia() {
- // mediaService.Delete(media);
- // await jsRuntime.InvokeVoidAsync("history.back");
- // }
+ await hb.Media.UpdateAsync(updatedMedia);
+
+ media = updatedMedia;
+ }
+
+ infoEditMode = false;
+ await InvokeAsync(() => StateHasChanged());
+ }
+
+ private async Task DeleteMedia() {
+ await hb.Media.DeleteAsync(MediaId);
+
+ deleteDialog.Hide();
+
+ // TODO: Use the NavigationManager properly
+ await jsRuntime.InvokeVoidAsync("history.back");
+ }
}