Fixed voice work supported language search. Include supported languages and original circle in the search result item. Several UI style updates.
This commit is contained in:
@@ -28,8 +28,10 @@ public record VoiceWorkSearchResult
|
|||||||
public byte SubtitleLanguage { get; init; }
|
public byte SubtitleLanguage { get; init; }
|
||||||
public bool? IsValid { get; init; }
|
public bool? IsValid { get; init; }
|
||||||
public required VoiceWorkCircleItem Circle { get; set; }
|
public required VoiceWorkCircleItem Circle { get; set; }
|
||||||
|
public VoiceWorkCircleItem? OriginalCircle { get; set; }
|
||||||
public VoiceWorkTagItem[] Tags { get; set; } = [];
|
public VoiceWorkTagItem[] Tags { get; set; } = [];
|
||||||
public VoiceWorkCreatorItem[] Creators { get; set; } = [];
|
public VoiceWorkCreatorItem[] Creators { get; set; } = [];
|
||||||
|
public string[] SupportedLanguages { get; set; } = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public class VoiceWorkCircleItem
|
public class VoiceWorkCircleItem
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using JSMR.Application.Enums;
|
|||||||
using JSMR.Application.VoiceWorks.Queries.Search;
|
using JSMR.Application.VoiceWorks.Queries.Search;
|
||||||
using JSMR.Domain.Entities;
|
using JSMR.Domain.Entities;
|
||||||
using JSMR.Domain.Enums;
|
using JSMR.Domain.Enums;
|
||||||
|
using JSMR.Domain.ValueObjects;
|
||||||
using JSMR.Infrastructure.Common.Queries;
|
using JSMR.Infrastructure.Common.Queries;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
@@ -60,9 +61,10 @@ public class VoiceWorkSearchProvider(AppDbContext context, IVoiceWorkFullTextSea
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (criteria.SupportedLanguages.Length > 0)
|
//if (criteria.SupportedLanguages.Length > 0)
|
||||||
filteredQuery = filteredQuery.Where(x => criteria.SupportedLanguages.AsEnumerable().Contains((Language)x.VoiceWork.SubtitleLanguage));
|
// filteredQuery = filteredQuery.Where(x => criteria.SupportedLanguages.AsEnumerable().Contains((Language)x.VoiceWork.SubtitleLanguage));
|
||||||
|
|
||||||
|
filteredQuery = ApplySupportedLanguageFilter(filteredQuery, criteria);
|
||||||
filteredQuery = ApplyCircleStatusFilter(filteredQuery, criteria);
|
filteredQuery = ApplyCircleStatusFilter(filteredQuery, criteria);
|
||||||
filteredQuery = ApplyTagStatusFilter(filteredQuery, criteria);
|
filteredQuery = ApplyTagStatusFilter(filteredQuery, criteria);
|
||||||
filteredQuery = ApplyCreatorStatusFilter(filteredQuery, criteria);
|
filteredQuery = ApplyCreatorStatusFilter(filteredQuery, criteria);
|
||||||
@@ -118,6 +120,26 @@ public class VoiceWorkSearchProvider(AppDbContext context, IVoiceWorkFullTextSea
|
|||||||
return query.Where(x => voiceWorkIds.Contains(x.VoiceWork.VoiceWorkId));
|
return query.Where(x => voiceWorkIds.Contains(x.VoiceWork.VoiceWorkId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private IQueryable<VoiceWorkQuery> ApplySupportedLanguageFilter(IQueryable<VoiceWorkQuery> query, VoiceWorkSearchCriteria criteria)
|
||||||
|
{
|
||||||
|
if (criteria.SupportedLanguages.Length == 0)
|
||||||
|
return query;
|
||||||
|
|
||||||
|
List<string> languageCodes = [];
|
||||||
|
|
||||||
|
foreach (Language language in criteria.SupportedLanguages)
|
||||||
|
{
|
||||||
|
if (SupportedLanguage.TryFromLanguage(language, out SupportedLanguage? supportedLanguage))
|
||||||
|
languageCodes.Add(supportedLanguage.Code);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (languageCodes.Count == 0)
|
||||||
|
return query;
|
||||||
|
|
||||||
|
return query.Where(q => context.VoiceWorkSupportedLanguages.Any(sl => sl.VoiceWorkId == q.VoiceWork.VoiceWorkId && languageCodes.Contains(sl.Language)));
|
||||||
|
}
|
||||||
|
|
||||||
private IQueryable<VoiceWorkQuery> ApplyCircleStatusFilter(IQueryable<VoiceWorkQuery> query, VoiceWorkSearchCriteria criteria)
|
private IQueryable<VoiceWorkQuery> ApplyCircleStatusFilter(IQueryable<VoiceWorkQuery> query, VoiceWorkSearchCriteria criteria)
|
||||||
{
|
{
|
||||||
if (criteria.CircleStatus is null)
|
if (criteria.CircleStatus is null)
|
||||||
@@ -432,6 +454,7 @@ public class VoiceWorkSearchProvider(AppDbContext context, IVoiceWorkFullTextSea
|
|||||||
PlannedReleaseDate = voiceWork.PlannedReleaseDate,
|
PlannedReleaseDate = voiceWork.PlannedReleaseDate,
|
||||||
Downloads = voiceWork.Downloads,
|
Downloads = voiceWork.Downloads,
|
||||||
WishlistCount = voiceWork.WishlistCount,
|
WishlistCount = voiceWork.WishlistCount,
|
||||||
|
Rating = (AgeRating)voiceWork.Rating,
|
||||||
Status = voiceWork.Status,
|
Status = voiceWork.Status,
|
||||||
SubtitleLanguage = voiceWork.SubtitleLanguage,
|
SubtitleLanguage = voiceWork.SubtitleLanguage,
|
||||||
HasTrial = voiceWork.HasTrial,
|
HasTrial = voiceWork.HasTrial,
|
||||||
@@ -449,16 +472,24 @@ public class VoiceWorkSearchProvider(AppDbContext context, IVoiceWorkFullTextSea
|
|||||||
|
|
||||||
int[] voiceWorkIds = [.. items.Select(i => i.VoiceWorkId)];
|
int[] voiceWorkIds = [.. items.Select(i => i.VoiceWorkId)];
|
||||||
|
|
||||||
Dictionary<int, VoiceWorkTagItem[]> tagsByVw = await GetTagsAsync(voiceWorkIds, cancellationToken);
|
var tagsByVoiceWork = await GetTagsAsync(voiceWorkIds, cancellationToken);
|
||||||
Dictionary<int, VoiceWorkCreatorItem[]> creatorsByVw = await GetCreatorsAsync(voiceWorkIds, cancellationToken);
|
var creatorsByVoiceWork = await GetCreatorsAsync(voiceWorkIds, cancellationToken);
|
||||||
|
var supportedLanguagesByVoiceWork = await GetSupportedLanguagesAsync(voiceWorkIds, cancellationToken);
|
||||||
|
var originalCirclesByVoiceWork = await GetOriginalCircles(voiceWorkIds, cancellationToken);
|
||||||
|
|
||||||
foreach (VoiceWorkSearchResult item in items)
|
foreach (VoiceWorkSearchResult item in items)
|
||||||
{
|
{
|
||||||
if (tagsByVw.TryGetValue(item.VoiceWorkId, out VoiceWorkTagItem[]? tags))
|
if (tagsByVoiceWork.TryGetValue(item.VoiceWorkId, out VoiceWorkTagItem[]? tags))
|
||||||
item.Tags = tags;
|
item.Tags = tags;
|
||||||
|
|
||||||
if (creatorsByVw.TryGetValue(item.VoiceWorkId, out VoiceWorkCreatorItem[]? creators))
|
if (creatorsByVoiceWork.TryGetValue(item.VoiceWorkId, out VoiceWorkCreatorItem[]? creators))
|
||||||
item.Creators = creators;
|
item.Creators = creators;
|
||||||
|
|
||||||
|
if (supportedLanguagesByVoiceWork.TryGetValue(item.VoiceWorkId, out string[]? supportedLanguages))
|
||||||
|
item.SupportedLanguages = supportedLanguages;
|
||||||
|
|
||||||
|
if (originalCirclesByVoiceWork.TryGetValue(item.VoiceWorkId, out VoiceWorkCircleItem? voiceWorkCircleItem))
|
||||||
|
item.OriginalCircle = voiceWorkCircleItem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -499,4 +530,51 @@ public class VoiceWorkSearchProvider(AppDbContext context, IVoiceWorkFullTextSea
|
|||||||
g => g.Select(r => new VoiceWorkCreatorItem { CreatorId = r.CreatorId, Name = r.Name, IsFavorite = r.Favorite, IsBlacklisted = r.Blacklisted }).ToArray()
|
g => g.Select(r => new VoiceWorkCreatorItem { CreatorId = r.CreatorId, Name = r.Name, IsFavorite = r.Favorite, IsBlacklisted = r.Blacklisted }).ToArray()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<Dictionary<int, string[]>> GetSupportedLanguagesAsync(int[] voiceWorkIds, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var supportedLanguageRows = await (
|
||||||
|
from voiceWorkSupportedLanguage in context.VoiceWorkSupportedLanguages.AsNoTracking()
|
||||||
|
where voiceWorkIds.Contains(voiceWorkSupportedLanguage.VoiceWorkId)
|
||||||
|
select new { voiceWorkSupportedLanguage.VoiceWorkId, voiceWorkSupportedLanguage.Language }
|
||||||
|
).ToListAsync(cancellationToken);
|
||||||
|
|
||||||
|
return supportedLanguageRows
|
||||||
|
.GroupBy(r => r.VoiceWorkId)
|
||||||
|
.ToDictionary(
|
||||||
|
g => g.Key,
|
||||||
|
g => g.Select(r => r.Language).ToArray()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<Dictionary<int, VoiceWorkCircleItem>> GetOriginalCircles(int[] voiceWorkIds, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var originalCircleRows = await (
|
||||||
|
from voiceWork in context.VoiceWorks.AsNoTracking()
|
||||||
|
join orignalVoiceWork in context.VoiceWorks.AsNoTracking() on voiceWork.OriginalProductId equals orignalVoiceWork.ProductId
|
||||||
|
join originalCircle in context.Circles.AsNoTracking() on orignalVoiceWork.CircleId equals originalCircle.CircleId
|
||||||
|
where voiceWorkIds.Contains(voiceWork.VoiceWorkId)
|
||||||
|
select new
|
||||||
|
{
|
||||||
|
voiceWork.VoiceWorkId,
|
||||||
|
originalCircle.Name,
|
||||||
|
originalCircle.MakerId,
|
||||||
|
originalCircle.Favorite,
|
||||||
|
originalCircle.Blacklisted
|
||||||
|
}
|
||||||
|
).ToListAsync(cancellationToken);
|
||||||
|
|
||||||
|
return originalCircleRows
|
||||||
|
.GroupBy(r => r.VoiceWorkId)
|
||||||
|
.ToDictionary(
|
||||||
|
g => g.Key,
|
||||||
|
g => g.Select(r => new VoiceWorkCircleItem()
|
||||||
|
{
|
||||||
|
Name = r.Name,
|
||||||
|
MakerId = r.MakerId,
|
||||||
|
IsFavorite = r.Favorite,
|
||||||
|
IsBlacklisted = r.Blacklisted
|
||||||
|
}).First()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -3,12 +3,12 @@
|
|||||||
@if (string.IsNullOrWhiteSpace(Url))
|
@if (string.IsNullOrWhiteSpace(Url))
|
||||||
{
|
{
|
||||||
<div class="@GetClasses()" @onclick="@OnClickAsync">
|
<div class="@GetClasses()" @onclick="@OnClickAsync">
|
||||||
@if (Graphic != null)
|
@if (Graphic != null && Graphic != Enums.Graphic.None)
|
||||||
{
|
{
|
||||||
<Icon Graphic="@Graphic.Value"
|
<Icon Graphic="@Graphic.Value"
|
||||||
Varient="@(IconVarient ?? Enums.IconVarient.None)"
|
Varient="@(IconVarient ?? Enums.IconVarient.None)"
|
||||||
Size="@(IconSize ?? Enums.SizeVarient.Small)"
|
Size="@(IconSize ?? Enums.SizeVarient.Small)"
|
||||||
Color="@Color">
|
UseCurrentColor>
|
||||||
</Icon>
|
</Icon>
|
||||||
}
|
}
|
||||||
<span>@ChildContent</span>
|
<span>@ChildContent</span>
|
||||||
@@ -23,7 +23,7 @@ else
|
|||||||
Graphic="@Graphic.Value"
|
Graphic="@Graphic.Value"
|
||||||
Varient="@(IconVarient ?? Enums.IconVarient.None)"
|
Varient="@(IconVarient ?? Enums.IconVarient.None)"
|
||||||
Size="@(IconSize ?? Enums.SizeVarient.Small)"
|
Size="@(IconSize ?? Enums.SizeVarient.Small)"
|
||||||
Color="@Color">
|
UseCurrentColor>
|
||||||
</Icon>
|
</Icon>
|
||||||
}
|
}
|
||||||
<span>@ChildContent</span>
|
<span>@ChildContent</span>
|
||||||
@@ -94,6 +94,11 @@ else
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Varient == ElementVarient.Filled && Tone == ToneVarient.Tint)
|
||||||
|
{
|
||||||
|
classNames.Add($"varient-tint");
|
||||||
|
}
|
||||||
|
|
||||||
if (Click.HasDelegate || string.IsNullOrWhiteSpace(Url) == false)
|
if (Click.HasDelegate || string.IsNullOrWhiteSpace(Url) == false)
|
||||||
{
|
{
|
||||||
classNames.Add("is-clickable");
|
classNames.Add("is-clickable");
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
@using JSMR.UI.Blazor.Enums
|
@using JSMR.UI.Blazor.Enums
|
||||||
|
|
||||||
<div class="@GetIconClasses()"></div>
|
<div class="@GetIconClasses()" style="@GetStyle()"></div>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[Parameter]
|
[Parameter]
|
||||||
@@ -15,20 +15,33 @@
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public ColorVarient Color { get; set; }
|
public ColorVarient Color { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public bool UseCurrentColor { get; set; }
|
||||||
|
|
||||||
private string GetIconClasses()
|
private string GetIconClasses()
|
||||||
{
|
{
|
||||||
string graphic = Varient == IconVarient.None
|
string graphic = Varient == IconVarient.None
|
||||||
? Graphic.ToString().ToLower()
|
? Graphic.ToString().ToLower()
|
||||||
: $"{Graphic.ToString().ToLower()}-{Varient.ToString().ToLower()}";
|
: $"{Graphic.ToString().ToLower()}-{Varient.ToString().ToLower()}";
|
||||||
|
|
||||||
List<string> classNames =
|
List<string> classNames =
|
||||||
[
|
[
|
||||||
$"j-icon",
|
$"j-icon",
|
||||||
$"j-icon-{graphic}",
|
$"j-icon-{graphic}",
|
||||||
$"size-{Size.ToString().ToLower()}",
|
$"size-{Size.ToString().ToLower()}",
|
||||||
$"background-color-{Color.ToString().ToLower()}"
|
//$"background-color-{Color.ToString().ToLower()}"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (!UseCurrentColor)
|
||||||
|
{
|
||||||
|
classNames.Add($"background-color-{Color.ToString().ToLower()}");
|
||||||
|
}
|
||||||
|
|
||||||
return string.Join(" ", classNames);
|
return string.Join(" ", classNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string? GetStyle()
|
||||||
|
{
|
||||||
|
return UseCurrentColor ? "background-color: currentColor;" : null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -15,34 +15,66 @@
|
|||||||
<div class="j-product-title">
|
<div class="j-product-title">
|
||||||
<a href="@Product.ProductUrl" target="_blank">@Product.ProductName</a>
|
<a href="@Product.ProductUrl" target="_blank">@Product.ProductName</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="j-product-contributors">
|
<BitStack Horizontal="true" Gap="0.5em" AutoHeight Wrap>
|
||||||
<span class="j-circle">
|
@* <MudChip T="string"
|
||||||
<MudChip T="string"
|
|
||||||
Href=@($"https://www.dlsite.com/maniax/circle/profile/=/maker_id/{Product.MakerId}.html")
|
Href=@($"https://www.dlsite.com/maniax/circle/profile/=/maker_id/{Product.MakerId}.html")
|
||||||
Target="_blank"
|
Target="_blank"
|
||||||
Variant="MudBlazor.Variant.Filled"
|
Variant="MudBlazor.Variant.Filled"
|
||||||
Icon="@Icons.Material.Outlined.Circle">@Product.Maker</MudChip>
|
Icon="@Icons.Material.Outlined.Circle">@Product.Maker</MudChip> *@
|
||||||
@* <CircleChip Circle="@Product.Circle"></CircleChip> *@
|
@* <CircleChip Circle="@Product.Circle"></CircleChip> *@
|
||||||
@foreach (var creator in Product.Creators)
|
<Chip Graphic="Graphic.Circle" Varient="ElementVarient.Outlined" Color="ColorVarient.Secondary" Url=@($"https://www.dlsite.com/maniax/circle/profile/=/maker_id/{Product.MakerId}.html")>@Product.Maker</Chip>
|
||||||
{
|
|
||||||
<MudChip T="string"
|
@if (Product.OriginalCircle is not null)
|
||||||
|
{
|
||||||
|
<Chip Graphic="Graphic.Circle" Varient="ElementVarient.Outlined" Color="ColorVarient.Secondary" Url=@($"https://www.dlsite.com/maniax/circle/profile/=/maker_id/{Product.OriginalCircle.MakerId}.html")>@Product.OriginalCircle.Name</Chip>
|
||||||
|
}
|
||||||
|
|
||||||
|
@foreach (var creator in Product.Creators)
|
||||||
|
{
|
||||||
|
@* <MudChip T="string"
|
||||||
Href=@($"https://www.dlsite.com/maniax/fsr/=/keyword_creater/{creator.Name}")
|
Href=@($"https://www.dlsite.com/maniax/fsr/=/keyword_creater/{creator.Name}")
|
||||||
Target="_blank"
|
Target="_blank"
|
||||||
Variant="MudBlazor.Variant.Filled"
|
Variant="MudBlazor.Variant.Filled"
|
||||||
Icon="@Icons.Material.Filled.Person">@creator.Name</MudChip>
|
Icon="@Icons.Material.Filled.Person">@creator.Name</MudChip> *@
|
||||||
@* <CreatorChip Creator="@creator"></CreatorChip> *@
|
@* <CreatorChip Creator="@creator"></CreatorChip> *@
|
||||||
}
|
<Chip Graphic="Graphic.Person" Varient="ElementVarient.Outlined" IconVarient="IconVarient.Fill" Color="ColorVarient.Secondary" Url=@($"https://www.dlsite.com/maniax/fsr/=/keyword_creater/{creator.Name}")>@creator.Name</Chip>
|
||||||
</span>
|
}
|
||||||
</div>
|
</BitStack>
|
||||||
|
<BitStack Horizontal="true" Gap="0.5em" AutoHeight Wrap>
|
||||||
|
@foreach (string supportedLanguage in Product.SupportedLanguages)
|
||||||
|
{
|
||||||
|
<Chip Graphic="Graphic.Globe" Varient="ElementVarient.Outlined" Color="ColorVarient.Secondary">@GetSupportedLanguageDescription(supportedLanguage)</Chip>
|
||||||
|
}
|
||||||
|
@if (Product.OriginalProductId is not null)
|
||||||
|
{
|
||||||
|
<Chip Graphic="Graphic.Translate" Varient="ElementVarient.Outlined" Color="ColorVarient.Secondary">Translation</Chip>
|
||||||
|
}
|
||||||
|
@if (Product.Rating == AgeRating.AllAges)
|
||||||
|
{
|
||||||
|
<Chip Graphic="Graphic.Age" Color="ColorVarient.Green" Varient="ElementVarient.Outlined">All Ages</Chip>
|
||||||
|
}
|
||||||
|
else if (Product.Rating == AgeRating.R15)
|
||||||
|
{
|
||||||
|
<Chip Graphic="Graphic.Age" Color="ColorVarient.Blue" Varient="ElementVarient.Outlined">R-15</Chip>
|
||||||
|
}
|
||||||
|
@if (Product.HasTrial || Product.HasChobit)
|
||||||
|
{
|
||||||
|
<Chip Graphic="Graphic.Download" Color="ColorVarient.Yellow" Varient="ElementVarient.Outlined">Trial</Chip>
|
||||||
|
}
|
||||||
|
@if (Product.Favorite)
|
||||||
|
{
|
||||||
|
<Chip Graphic="Graphic.Star" Color="ColorVarient.Teal" Varient="ElementVarient.Outlined">Favorite</Chip>
|
||||||
|
}
|
||||||
|
</BitStack>
|
||||||
<div class="j-product-description">@Product.Description</div>
|
<div class="j-product-description">@Product.Description</div>
|
||||||
<div class="j-tags">
|
<div class="j-tags">
|
||||||
@foreach (var tag in Product.Tags)
|
@foreach (var tag in Product.Tags)
|
||||||
{
|
{
|
||||||
@* <div class="j-tag">@tag.Name</div> *@
|
@* <div class="j-tag">@tag.Name</div> *@
|
||||||
<ProductTag Tag="tag"></ProductTag>
|
<ProductTag Tag="tag"></ProductTag>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@* <div class="j-tags">
|
@* <div class="j-tags">
|
||||||
@foreach (var tag in Product.Tags)
|
@foreach (var tag in Product.Tags)
|
||||||
{
|
{
|
||||||
<TagChip Tag="tag"></TagChip>
|
<TagChip Tag="tag"></TagChip>
|
||||||
@@ -149,4 +181,17 @@
|
|||||||
return "jp";
|
return "jp";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string GetSupportedLanguageDescription(string code)
|
||||||
|
{
|
||||||
|
switch (code)
|
||||||
|
{
|
||||||
|
case "JPN": return "Japanese";
|
||||||
|
case "ENG": return "English";
|
||||||
|
case "CHI_HANS": return "Chinese (Simplified)";
|
||||||
|
case "CHI_HANT": return "Chinese (Traditional)";
|
||||||
|
case "KO_KR": return "Korean";
|
||||||
|
default: return code;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
@using JSMR.Application.Tags.Queries.Search.Contracts
|
@using JSMR.Application.Tags.Queries.Search.Contracts
|
||||||
@using JSMR.Application.VoiceWorks.Queries.Search
|
@using JSMR.Application.VoiceWorks.Queries.Search
|
||||||
|
@using JSMR.UI.Blazor.Enums
|
||||||
@using JSMR.UI.Blazor.Filters
|
@using JSMR.UI.Blazor.Filters
|
||||||
@using JSMR.UI.Blazor.Services
|
@using JSMR.UI.Blazor.Services
|
||||||
@using Microsoft.AspNetCore.WebUtilities
|
@using Microsoft.AspNetCore.WebUtilities
|
||||||
|
|
||||||
<a class="@Classes" @onclick="@OnClick"><Icon Graphic="Enums.Graphic.Tag" Color="Enums.ColorVarient.Primary"></Icon>@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> *@
|
@* <MudChip T="string" Icon="@Icons.Material.Outlined.Tag" @onclick="@OnClick" Variant="@MudBlazor.Variant.Filled" Color="@MudBlazor.Color.Surface">@Tag.Name</MudChip> *@
|
||||||
|
<Chip Graphic="Graphic.Tag" Varient="ElementVarient.Outlined" Color="@GetColorVarient()" Tone="ToneVarient.None" Click="@OnClick">@Tag.Name</Chip>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[Inject]
|
[Inject]
|
||||||
@@ -33,6 +35,21 @@
|
|||||||
return string.Join(" ", classNames);
|
return string.Join(" ", classNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ColorVarient GetColorVarient()
|
||||||
|
{
|
||||||
|
if (Tag.IsFavorite)
|
||||||
|
{
|
||||||
|
return ColorVarient.Mint;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Tag.IsFavorite)
|
||||||
|
{
|
||||||
|
return ColorVarient.Orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ColorVarient.Primary;
|
||||||
|
}
|
||||||
|
|
||||||
private void OnClick()
|
private void OnClick()
|
||||||
{
|
{
|
||||||
VoiceWorkFilterState state = new()
|
VoiceWorkFilterState state = new()
|
||||||
|
|||||||
@@ -19,5 +19,7 @@ public enum Graphic
|
|||||||
Sort,
|
Sort,
|
||||||
Grid,
|
Grid,
|
||||||
Age,
|
Age,
|
||||||
Calendar
|
Calendar,
|
||||||
|
Download,
|
||||||
|
Microphone
|
||||||
}
|
}
|
||||||
@@ -3,32 +3,42 @@ using JSMR.Application.Creators.Queries.Search;
|
|||||||
using JSMR.Application.Tags.Queries.Search;
|
using JSMR.Application.Tags.Queries.Search;
|
||||||
using JSMR.Application.VoiceWorks.Queries.Search;
|
using JSMR.Application.VoiceWorks.Queries.Search;
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace JSMR.UI.Blazor.Services;
|
namespace JSMR.UI.Blazor.Services;
|
||||||
|
|
||||||
public class VoiceWorksClient(HttpClient http)
|
public class VoiceWorksClient(HttpClient http)
|
||||||
{
|
{
|
||||||
|
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web)
|
||||||
|
{
|
||||||
|
Converters =
|
||||||
|
{
|
||||||
|
new JsonStringEnumConverter()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public async Task<SearchVoiceWorksResponse?> SearchAsync(SearchVoiceWorksRequest request, CancellationToken ct = default)
|
public async Task<SearchVoiceWorksResponse?> SearchAsync(SearchVoiceWorksRequest request, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
using var resp = await http.PostAsJsonAsync("/api/voiceworks/search", request, ct);
|
using var resp = await http.PostAsJsonAsync("/api/voiceworks/search", request, ct);
|
||||||
return await resp.Content.ReadFromJsonAsync<SearchVoiceWorksResponse>(cancellationToken: ct);
|
return await resp.Content.ReadFromJsonAsync<SearchVoiceWorksResponse>(JsonOptions, cancellationToken: ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<SearchCirclesResponse?> SearchAsync(SearchCirclesRequest request, CancellationToken ct = default)
|
public async Task<SearchCirclesResponse?> SearchAsync(SearchCirclesRequest request, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
using var resp = await http.PostAsJsonAsync("/api/circles/search", request, ct);
|
using var resp = await http.PostAsJsonAsync("/api/circles/search", request, ct);
|
||||||
return await resp.Content.ReadFromJsonAsync<SearchCirclesResponse>(cancellationToken: ct);
|
return await resp.Content.ReadFromJsonAsync<SearchCirclesResponse>(JsonOptions, cancellationToken: ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<SearchCreatorsResponse?> SearchAsync(SearchCreatorsRequest request, CancellationToken ct = default)
|
public async Task<SearchCreatorsResponse?> SearchAsync(SearchCreatorsRequest request, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
using var resp = await http.PostAsJsonAsync("/api/creators/search", request, ct);
|
using var resp = await http.PostAsJsonAsync("/api/creators/search", request, ct);
|
||||||
return await resp.Content.ReadFromJsonAsync<SearchCreatorsResponse>(cancellationToken: ct);
|
return await resp.Content.ReadFromJsonAsync<SearchCreatorsResponse>(JsonOptions, cancellationToken: ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<SearchTagsResponse?> SearchAsync(SearchTagsRequest request, CancellationToken ct = default)
|
public async Task<SearchTagsResponse?> SearchAsync(SearchTagsRequest request, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
using var resp = await http.PostAsJsonAsync("/api/tags/search", request, ct);
|
using var resp = await http.PostAsJsonAsync("/api/tags/search", request, ct);
|
||||||
return await resp.Content.ReadFromJsonAsync<SearchTagsResponse>(cancellationToken: ct);
|
return await resp.Content.ReadFromJsonAsync<SearchTagsResponse>(JsonOptions, cancellationToken: ct);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -592,6 +592,7 @@ code {
|
|||||||
.j-tags {
|
.j-tags {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: .75rem .5rem;
|
gap: .75rem .5rem;
|
||||||
|
gap: .5rem .5rem;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
/*gap: .5rem 1rem;*/
|
/*gap: .5rem 1rem;*/
|
||||||
}
|
}
|
||||||
@@ -647,41 +648,70 @@ code {
|
|||||||
.j-chip {
|
.j-chip {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: .4rem;
|
gap: .5em;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: .8rem;
|
font-size: .8rem;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
|
/*--chip-rgb: var(--secondary-rgb, 148 163 184);
|
||||||
|
--chip-tint-alpha: 0.12;*/
|
||||||
|
|
||||||
--chip-rgb: var(--secondary-rgb, 148 163 184);
|
--chip-rgb: var(--secondary-rgb, 148 163 184);
|
||||||
|
--chip-bg-rgb: transparent;
|
||||||
|
--chip-fg-rgb: var(--chip-rgb);
|
||||||
|
--chip-border-rgb: transparent;
|
||||||
--chip-tint-alpha: 0.12;
|
--chip-tint-alpha: 0.12;
|
||||||
|
color: rgb(var(--chip-fg-rgb));
|
||||||
|
background: rgb(var(--chip-bg-rgb));
|
||||||
|
border: 1px solid rgb(var(--chip-border-rgb));
|
||||||
|
padding: .5em 1em;
|
||||||
|
border-radius: 1rem;
|
||||||
|
border: 1px solid transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.j-chip.is-clickable {
|
.j-chip.is-clickable {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
--chip-hover-alpha: 0.2;
|
transition: background .2s linear, color .2s linear, border-color .2s linear, filter .2s linear;
|
||||||
transition: .2s linear;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.j-chip.is-clickable:hover {
|
.j-chip.is-clickable:hover {
|
||||||
background: rgb(var(--chip-rgb) / var(--chip-hover-alpha));
|
filter: brightness(1.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.j-chip.varient-filled {
|
.j-chip.varient-filled {
|
||||||
padding: .4rem .8rem;
|
--chip-bg-rgb: var(--chip-rgb);
|
||||||
border-radius: 1rem;
|
--chip-border-rgb: var(--chip-rgb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*.j-chip.varient-outlined {
|
||||||
|
--chip-bg-rgb: transparent;
|
||||||
|
--chip-fg-rgb: var(--chip-rgb);
|
||||||
|
--chip-border-rgb: var(--chip-rgb);
|
||||||
|
}*/
|
||||||
|
|
||||||
.j-chip.varient-outlined {
|
.j-chip.varient-outlined {
|
||||||
border-width: 1px;
|
background: rgba(0,0,0,.35);
|
||||||
border-style: solid;
|
border: 1px solid rgb(var(--chip-rgb) / 0.65);
|
||||||
padding: .4rem .8rem;
|
|
||||||
border-radius: 1rem;
|
|
||||||
border-color: rgb(var(--chip-rgb));
|
|
||||||
color: rgb(var(--chip-rgb));
|
color: rgb(var(--chip-rgb));
|
||||||
background: transparent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* .j-chip.varient-tint {
|
||||||
|
--chip-bg-rgb: var(--chip-rgb) / var(--chip-tint-alpha);
|
||||||
|
--chip-fg-rgb: var(--chip-rgb);
|
||||||
|
--chip-border-rgb: transparent;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
.j-chip.varient-tint {
|
||||||
|
--chip-bg-alpha: 0.14;
|
||||||
|
--chip-border-alpha: 0.32;
|
||||||
|
color: rgb(var(--chip-rgb));
|
||||||
|
background: rgb(var(--chip-rgb) / var(--chip-bg-alpha));
|
||||||
|
/*border: 1px solid rgb(var(--chip-rgb) / var(--chip-border-alpha));*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Old ? */
|
||||||
.j-chip.tone-tint {
|
.j-chip.tone-tint {
|
||||||
background: rgb(var(--chip-rgb) / var(--chip-tint-alpha));
|
background: rgb(var(--chip-rgb) / var(--chip-tint-alpha));
|
||||||
}
|
}
|
||||||
@@ -698,6 +728,74 @@ code {
|
|||||||
--chip-rgb: var(--rgb-teal);
|
--chip-rgb: var(--rgb-teal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* New */
|
||||||
|
.j-chip.color-primary {
|
||||||
|
--chip-rgb: var(--rgb-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.j-chip.varient-filled.color-primary {
|
||||||
|
--chip-fg-rgb: var(--rgb-on-primary, 0 0 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.j-chip.color-secondary {
|
||||||
|
--chip-rgb: var(--rgb-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.j-chip.varient-filled.color-secondary {
|
||||||
|
--chip-fg-rgb: var(--rgb-on-secondary, 0 0 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.j-chip.color-mint {
|
||||||
|
--chip-rgb: var(--rgb-mint);
|
||||||
|
}
|
||||||
|
|
||||||
|
.j-chip.varient-filled.color-mint {
|
||||||
|
--chip-fg-rgb: var(--rgb-on-mint, 0 0 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.j-chip.color-green {
|
||||||
|
--chip-rgb: var(--rgb-green);
|
||||||
|
}
|
||||||
|
|
||||||
|
.j-chip.varient-filled.color-green {
|
||||||
|
--chip-fg-rgb: var(--rgb-on-green, 255 255 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
.j-chip.color-yellow {
|
||||||
|
color: rgb(var(--chip-fg-rgb));
|
||||||
|
--chip-rgb: var(--rgb-yellow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.j-chip.varient-filled.color-yellow {
|
||||||
|
--chip-fg-rgb: var(--rgb-on-yellow, 255 255 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
.j-chip.color-pink {
|
||||||
|
color: rgb(var(--chip-fg-rgb));
|
||||||
|
--chip-rgb: var(--rgb-pink);
|
||||||
|
}
|
||||||
|
|
||||||
|
.j-chip.varient-filled.color-pink {
|
||||||
|
--chip-fg-rgb: var(--rgb-on-pink, 255 255 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
.j-chip.color-teal {
|
||||||
|
--chip-rgb: var(--rgb-teal);
|
||||||
|
}
|
||||||
|
|
||||||
|
.j-chip.varient-filled.color-teal {
|
||||||
|
--chip-fg-rgb: var(--rgb-on-teal, 255 255 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
.j-chip.color-black {
|
||||||
|
color: inherit;
|
||||||
|
--chip-rgb: 39 39 39;
|
||||||
|
}
|
||||||
|
|
||||||
|
.j-chip.varient-filled.color-black {
|
||||||
|
--chip-fg-rgb: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
/* Icons */
|
/* Icons */
|
||||||
.j-icon {
|
.j-icon {
|
||||||
mask-repeat: no-repeat;
|
mask-repeat: no-repeat;
|
||||||
@@ -825,6 +923,22 @@ code {
|
|||||||
mask-image: url("../svg/age-rating.svg");
|
mask-image: url("../svg/age-rating.svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.j-icon-download {
|
||||||
|
mask-image: url("../svg/cloud-download.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
.j-icon-download-fill {
|
||||||
|
mask-image: url("../svg/cloud-download-fill.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
.j-icon-microphone {
|
||||||
|
mask-image: url("../svg/microphone.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
.j-icon-microphone-fill {
|
||||||
|
mask-image: url("../svg/microphone-fill.svg");
|
||||||
|
}
|
||||||
|
|
||||||
.j-icon-2 {
|
.j-icon-2 {
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
|
|||||||
@@ -40,9 +40,15 @@
|
|||||||
--input-focus-border-color: #64b5f6;
|
--input-focus-border-color: #64b5f6;
|
||||||
--input-focus-box-shadow: 0 0 0 1px #93cbf9;
|
--input-focus-box-shadow: 0 0 0 1px #93cbf9;
|
||||||
/* RGB Tokens */
|
/* RGB Tokens */
|
||||||
|
--rgb-primary: 180 200 214;
|
||||||
|
--rgb-secondary: 200 220 234;
|
||||||
--rgb-mint: 167 243 208;
|
--rgb-mint: 167 243 208;
|
||||||
--rgb-green: 175 224 125;
|
--rgb-green: 175 224 125;
|
||||||
--rgb-teal: 110 236 255;
|
--rgb-teal: 110 236 255;
|
||||||
|
--rgb-yellow: 255 224 115;
|
||||||
|
--rgb-on-yellow: 0 0 0;
|
||||||
|
--rgb-pink: 224 104 148;
|
||||||
|
--rgb-on-pink: 255 255 255;
|
||||||
/* Colors */
|
/* Colors */
|
||||||
--color-primary: rgb(180,200, 214);
|
--color-primary: rgb(180,200, 214);
|
||||||
--color-secondary: rgb(200,220,234);
|
--color-secondary: rgb(200,220,234);
|
||||||
|
|||||||
3
JSMR.UI.Blazor/wwwroot/svg/cloud-download-fill.svg
Normal file
3
JSMR.UI.Blazor/wwwroot/svg/cloud-download-fill.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-cloud-download-fill" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd" d="M8 0a5.53 5.53 0 0 0-3.594 1.342c-.766.66-1.321 1.52-1.464 2.383C1.266 4.095 0 5.555 0 7.318 0 9.366 1.708 11 3.781 11H7.5V5.5a.5.5 0 0 1 1 0V11h4.188C14.502 11 16 9.57 16 7.773c0-1.636-1.242-2.969-2.834-3.194C12.923 1.999 10.69 0 8 0m-.354 15.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 14.293V11h-1v3.293l-2.146-2.147a.5.5 0 0 0-.708.708z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 525 B |
4
JSMR.UI.Blazor/wwwroot/svg/cloud-download.svg
Normal file
4
JSMR.UI.Blazor/wwwroot/svg/cloud-download.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-cloud-download" viewBox="0 0 16 16">
|
||||||
|
<path d="M4.406 1.342A5.53 5.53 0 0 1 8 0c2.69 0 4.923 2 5.166 4.579C14.758 4.804 16 6.137 16 7.773 16 9.569 14.502 11 12.687 11H10a.5.5 0 0 1 0-1h2.688C13.979 10 15 8.988 15 7.773c0-1.216-1.02-2.228-2.313-2.228h-.5v-.5C12.188 2.825 10.328 1 8 1a4.53 4.53 0 0 0-2.941 1.1c-.757.652-1.153 1.438-1.153 2.055v.448l-.445.049C2.064 4.805 1 5.952 1 7.318 1 8.785 2.23 10 3.781 10H6a.5.5 0 0 1 0 1H3.781C1.708 11 0 9.366 0 7.318c0-1.763 1.266-3.223 2.942-3.593.143-.863.698-1.723 1.464-2.383"/>
|
||||||
|
<path d="M7.646 15.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 14.293V5.5a.5.5 0 0 0-1 0v8.793l-2.146-2.147a.5.5 0 0 0-.708.708z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 768 B |
4
JSMR.UI.Blazor/wwwroot/svg/microphone-fill.svg
Normal file
4
JSMR.UI.Blazor/wwwroot/svg/microphone-fill.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-mic-fill" viewBox="0 0 16 16">
|
||||||
|
<path d="M5 3a3 3 0 0 1 6 0v5a3 3 0 0 1-6 0z"/>
|
||||||
|
<path d="M3.5 6.5A.5.5 0 0 1 4 7v1a4 4 0 0 0 8 0V7a.5.5 0 0 1 1 0v1a5 5 0 0 1-4.5 4.975V15h3a.5.5 0 0 1 0 1h-7a.5.5 0 0 1 0-1h3v-2.025A5 5 0 0 1 3 8V7a.5.5 0 0 1 .5-.5"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 354 B |
4
JSMR.UI.Blazor/wwwroot/svg/microphone.svg
Normal file
4
JSMR.UI.Blazor/wwwroot/svg/microphone.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-mic" viewBox="0 0 16 16">
|
||||||
|
<path d="M3.5 6.5A.5.5 0 0 1 4 7v1a4 4 0 0 0 8 0V7a.5.5 0 0 1 1 0v1a5 5 0 0 1-4.5 4.975V15h3a.5.5 0 0 1 0 1h-7a.5.5 0 0 1 0-1h3v-2.025A5 5 0 0 1 3 8V7a.5.5 0 0 1 .5-.5"/>
|
||||||
|
<path d="M10 8a2 2 0 1 1-4 0V3a2 2 0 1 1 4 0zM8 0a3 3 0 0 0-3 3v5a3 3 0 0 0 6 0V3a3 3 0 0 0-3-3"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 400 B |
Reference in New Issue
Block a user