Added voice work image fallback. Added tag/creator/circle chip components. Updated voice work search response to include favorite/blacklisted flags for tags/creators/circles.
Some checks failed
ci / build-test (push) Has been cancelled
ci / publish-image (push) Has been cancelled

This commit is contained in:
2026-02-22 01:56:04 -05:00
parent 9f30ef446a
commit 8348603b13
17 changed files with 521 additions and 23 deletions

View File

@@ -1,12 +1,26 @@
@using JSMR.UI.Blazor.Enums
<div class="j-chip">
@if (Graphic != null)
{
<Icon Graphic="@Graphic.Value" Color="@Color"></Icon>
}
<span>@ChildContent</span>
</div>
@if (string.IsNullOrWhiteSpace(Url))
{
<div class="@GetClasses()" @onclick="@OnClickAsync">
@if (Graphic != null)
{
<Icon Graphic="@Graphic.Value" Varient="@(IconVarient ?? Enums.IconVarient.None)" Color="@Color"></Icon>
}
<span>@ChildContent</span>
</div>
}
else
{
<a class="@GetClasses()" href="@Url" target="@Target">
@if (Graphic != null)
{
<Icon Graphic="@Graphic.Value" Varient="@(IconVarient ?? Enums.IconVarient.None)" Color="@Color"></Icon>
}
<span>@ChildContent</span>
</a>
}
@code {
[Parameter]
@@ -15,6 +29,72 @@
[Parameter]
public Graphic? Graphic { get; set; }
[Parameter]
public IconVarient? IconVarient { get; set; }
[Parameter]
public ColorVarient Color { get; set; } = ColorVarient.Primary;
[Parameter]
public ElementVarient Varient { get; set; } = ElementVarient.None;
[Parameter]
public ToneVarient Tone { get; set; } = ToneVarient.None;
[Parameter]
public string? Url { get; set; }
[Parameter]
public string? Target { get; set; }
[Parameter]
public EventCallback Click { get; set; }
private string GetClasses()
{
string color = Color.ToString().ToLower();
List<string> classNames =
[
$"j-chip",
$"color-{color}"
];
switch (Varient)
{
case ElementVarient.Filled:
classNames.Add($"varient-filled");
//classNames.Add($"background-color-{color}");
break;
case ElementVarient.Outlined:
classNames.Add($"varient-outlined");
//classNames.Add($"border-color-{color}");
break;
}
switch (Tone)
{
case ToneVarient.Solid:
classNames.Add($"tone-solid");
break;
case ToneVarient.Tint:
classNames.Add($"tone-tint");
break;
}
if (Click.HasDelegate || string.IsNullOrWhiteSpace(Url) == false)
{
classNames.Add("is-clickable");
}
return string.Join(" ", classNames);
}
private async Task OnClickAsync()
{
if (Click.HasDelegate)
{
await Click.InvokeAsync();
}
}
}

View File

@@ -0,0 +1,43 @@
@using JSMR.Application.Tags.Queries.Search.Contracts
@using JSMR.Application.VoiceWorks.Queries.Search
@using JSMR.UI.Blazor.Enums
@using JSMR.UI.Blazor.Filters
@using JSMR.UI.Blazor.Services
@using Microsoft.AspNetCore.WebUtilities
<Chip Graphic="Graphic.Circle" Color="@GetColor()" Varient="ElementVarient.Outlined" Tone="@GetTone()" Url="@GetUrl()" Target="_blank">@Circle.Name</Chip>
@code {
[Parameter]
public required VoiceWorkCircleItem Circle { get; set; }
private string GetUrl()
{
return $"https://www.dlsite.com/maniax/circle/profile/=/maker_id/{Circle.MakerId}.html";
}
private ColorVarient GetColor()
{
if (Circle.IsFavorite)
{
return ColorVarient.Mint;
}
if (Circle.IsBlacklisted)
{
return ColorVarient.Pink;
}
return ColorVarient.Secondary;
}
private ToneVarient GetTone()
{
if (Circle.IsFavorite || Circle.IsBlacklisted)
{
return ToneVarient.Tint;
}
return ToneVarient.None;
}
}

View File

@@ -0,0 +1,43 @@
@using JSMR.Application.Tags.Queries.Search.Contracts
@using JSMR.Application.VoiceWorks.Queries.Search
@using JSMR.UI.Blazor.Enums
@using JSMR.UI.Blazor.Filters
@using JSMR.UI.Blazor.Services
@using Microsoft.AspNetCore.WebUtilities
<Chip Graphic="Graphic.Person" IconVarient="IconVarient.Fill" Color="@GetColor()" Varient="ElementVarient.Outlined" Tone="@GetTone()" Url="@GetUrl()" Target="_blank">@Creator.Name</Chip>
@code {
[Parameter]
public required VoiceWorkCreatorItem Creator { get; set; }
private string GetUrl()
{
return $"https://www.dlsite.com/maniax/fsr/=/keyword_creater/{Creator.Name}";
}
private ColorVarient GetColor()
{
if (Creator.IsFavorite)
{
return ColorVarient.Mint;
}
if (Creator.IsBlacklisted)
{
return ColorVarient.Pink;
}
return ColorVarient.Secondary;
}
private ToneVarient GetTone()
{
if (Creator.IsFavorite || Creator.IsBlacklisted)
{
return ToneVarient.Tint;
}
return ToneVarient.None;
}
}

View File

@@ -0,0 +1,54 @@
@using JSMR.Application.Tags.Queries.Search.Contracts
@using JSMR.Application.VoiceWorks.Queries.Search
@using JSMR.UI.Blazor.Enums
@using JSMR.UI.Blazor.Filters
@using JSMR.UI.Blazor.Services
@using Microsoft.AspNetCore.WebUtilities
<Chip Graphic="Graphic.Tag" Color="@GetColor()" Varient="ElementVarient.Outlined" Tone="@GetTone()" Click="@OnClick">@Tag.Name</Chip>
@code {
[Inject]
protected NavigationManager NavigationManager { get; set; } = default!;
[Parameter]
public required VoiceWorkTagItem Tag { get; set; }
private ColorVarient GetColor()
{
if (Tag.IsFavorite)
{
return ColorVarient.Mint;
}
if (Tag.IsBlacklisted)
{
return ColorVarient.Pink;
}
return ColorVarient.Secondary;
}
private ToneVarient GetTone()
{
if (Tag.IsFavorite || Tag.IsBlacklisted)
{
return ToneVarient.Tint;
}
return ToneVarient.None;
}
private void OnClick()
{
VoiceWorkFilterState state = new()
{
TagIds = [Tag.TagId]
};
string basePath = new Uri(NavigationManager.Uri).GetLeftPart(UriPartial.Authority);
string uri = QueryHelpers.AddQueryString($"{basePath}/voiceworks", state.ToQuery());
NavigationManager.NavigateTo(uri);
}
}

View File

@@ -1,6 +1,6 @@
<div class="@ContainerClassees">
<div class="@OverlayClasses"></div>
<img class="@ImageClasses" loading="@LoadingAttribute" src="@Source" @onload="OnImageLoaded">
<img class="@ImageClasses" loading="@LoadingAttribute" src="@currentSource" @onload="OnImageLoaded" @onerror="OnImageError">
</div>
@code {
@@ -8,7 +8,7 @@
public required string Source { get; set; }
[Parameter]
public string FallbackSource { get; set; } = "images/home/no_img_main.gif";
public string FallbackSource { get; set; } = "images/web/home/not_found_img_main.png"; // "images/web/home/no_img_main.gif";
[Parameter]
public bool LazyLoading { get; set; } = true;
@@ -22,6 +22,10 @@
[Parameter]
public string? ImageClass { get; set; }
private string? currentSource;
private bool hasSourceErrored = false;
private bool hasFallbackSourceErrored = false;
private bool _isLoaded;
private string? _lastSource;
@@ -33,6 +37,10 @@
protected override void OnParametersSet()
{
currentSource = Source;
hasSourceErrored = false;
hasFallbackSourceErrored = false;
if (!string.Equals(_lastSource, Source, StringComparison.Ordinal))
{
_lastSource = Source;
@@ -103,4 +111,20 @@
{
_isLoaded = true;
}
private void OnImageError()
{
if (!hasSourceErrored && !string.IsNullOrEmpty(FallbackSource))
{
hasSourceErrored = true;
currentSource = FallbackSource;
StateHasChanged();
}
else if (!hasFallbackSourceErrored)
{
hasFallbackSourceErrored = true;
currentSource = "images/web/home/not_found_img_main.png";
StateHasChanged();
}
}
}

View File

@@ -1,5 +1,6 @@
@using JSMR.Application.VoiceWorks.Queries.Search
@using JSMR.Domain.Enums
@using JSMR.UI.Blazor.Components.Chips
@using JSMR.UI.Blazor.Enums
@using JSMR.UI.Blazor.Filters
@using JSMR.UI.Blazor.Services
@@ -8,7 +9,7 @@
<div class=@GetCardClasses(Product)>
<div class="j-voice-work-image-container">
<JImage OverlayClass="j-voice-work-image-overlay" ImageClass="j-voice-work-image" Source="@ImageUrlProvider.GetImageUrl(Product, "main")"></JImage>
<JImage OverlayClass="j-voice-work-image-overlay" ImageClass="j-voice-work-image" Source="@ImageUrlProvider.GetImageUrl(Product, "main")" FallbackSource="@ImageUrlProvider.GetImageUrl(Product, "main", "webp")"></JImage>
</div>
<div class="j-voice-work-content">
<div class="j-product-title">
@@ -21,6 +22,7 @@
Target="_blank"
Variant="MudBlazor.Variant.Filled"
Icon="@Icons.Material.Outlined.Circle">@Product.Maker</MudChip>
@* <CircleChip Circle="@Product.Circle"></CircleChip> *@
@foreach (var creator in Product.Creators)
{
<MudChip T="string"
@@ -28,6 +30,7 @@
Target="_blank"
Variant="MudBlazor.Variant.Filled"
Icon="@Icons.Material.Filled.Person">@creator.Name</MudChip>
@* <CreatorChip Creator="@creator"></CreatorChip> *@
}
</span>
</div>
@@ -39,6 +42,12 @@
<ProductTag Tag="tag"></ProductTag>
}
</div>
<div class="j-tags">
@foreach (var tag in Product.Tags)
{
@* <TagChip Tag="tag"></TagChip> *@
}
</div>
</div>
<div class="j-voice-work-info">
<div class="j-release-date-container">

View File

@@ -1,8 +1,10 @@
@using JSMR.Application.VoiceWorks.Queries.Search
@using JSMR.Application.Tags.Queries.Search.Contracts
@using JSMR.Application.VoiceWorks.Queries.Search
@using JSMR.UI.Blazor.Filters
@using JSMR.UI.Blazor.Services
@using Microsoft.AspNetCore.WebUtilities
<a class="j-tag" @onclick="@OnClick">@Tag.Name</a>
<a class="@Classes" @onclick="@OnClick"><Icon Graphic="Enums.Graphic.Tag" Color="Enums.ColorVarient.Primary"></Icon>@Tag.Name</a>
@* <MudChip T="string" Icon="@Icons.Material.Outlined.Tag" @onclick="@OnClick" Variant="@MudBlazor.Variant.Filled" Color="@MudBlazor.Color.Surface">@Tag.Name</MudChip> *@
@code {
@@ -12,6 +14,25 @@
[Parameter]
public required VoiceWorkTagItem Tag { get; set; }
private string Classes => GetClasses();
private string GetClasses()
{
List<string> classNames = ["j-tag", "j-tag-2"];
if (Tag.IsFavorite)
{
classNames.Add("j-tag-favorite");
}
if (Tag.IsBlacklisted)
{
classNames.Add("j-tag-blacklisted");
}
return string.Join(" ", classNames);
}
private void OnClick()
{
VoiceWorkFilterState state = new()