Updated CI for Blazor WebAssembly. Added styles for product cards.
All checks were successful
ci / build-test (push) Successful in 2m16s
ci / publish-image (push) Has been skipped

This commit is contained in:
2025-11-15 02:34:15 -05:00
parent 76284f2fab
commit 634050c06f
13 changed files with 289 additions and 94 deletions

View File

@@ -36,6 +36,9 @@ jobs:
- name: Docker sanity (ensures socket mount is working)
run: docker version
- name: Install WASM workload
run: dotnet workload install wasm-tools --skip-sign-check
- run: dotnet restore
- run: dotnet build --configuration Release --no-restore
- run: dotnet test --configuration Release --no-build --logger "trx;LogFileName=test-results.trx"

View File

@@ -7,5 +7,6 @@ public enum VoiceWorkSortField
Downloads,
WishlistCount,
SalesToWishlistRatio,
StarRating
StarRating,
FavoriteCircle
}

View File

@@ -324,6 +324,7 @@ public class VoiceWorkSearchProvider(AppDbContext context, IVoiceWorkFullTextSea
VoiceWorkSortField.Downloads => x => x.VoiceWork.Downloads ?? 0,
VoiceWorkSortField.WishlistCount => x => x.VoiceWork.WishlistCount ?? 0,
VoiceWorkSortField.StarRating => x => x.VoiceWork.StarRating ?? 0,
VoiceWorkSortField.FavoriteCircle => x => !x.Circle.Favorite,
_ => x => x.VoiceWork.ProductId
};

View File

@@ -0,0 +1,25 @@
@using JSMR.Application.VoiceWorks.Queries.Search
@using JSMR.UI.Blazor.Services
<div class="j-card j-voice-work-card">
<div class="j-voice-work-image-container">
<JImage ImageClass="j-voice-work-image" Source="@ImageUrlProvider.GetImageURL(Product.ProductId, Product.HasImage, Product.SalesDate, "main")"></JImage>
</div>
<div class="j-voice-work-content">
<div class="j-product-title">@Product.ProductName</div>
<div class="j-product-description">@Product.Description</div>
<div class="j-tags">
@foreach (var tag in Product.Tags)
{
<div class="j-tag">@tag.Name</div>
}
</div>
</div>
<div class="j-voice-work-info">
</div>
</div>
@code {
[Parameter]
public required VoiceWorkSearchResult Product { get; set; }
}

View File

@@ -0,0 +1,24 @@
@using JSMR.Application.VoiceWorks.Queries.Search
@if (Products is null)
{
<p>Loading…</p>
}
else if (Products.Length == 0)
{
<p>No results.</p>
}
else
{
<div class="j-product-items-container">
@foreach (var product in Products)
{
<JProduct Product="@product"></JProduct>
}
</div>
}
@code {
[Parameter]
public VoiceWorkSearchResult[]? Products { get; set; }
}

View File

@@ -0,0 +1,17 @@
namespace JSMR.UI.Blazor.Enums;
/// <summary>Image size selector for DLsite assets.</summary>
public enum ImageSize
{
/// <summary>100×100 square thumbnail (DLsite: "sam").</summary>
Thumb100,
/// <summary>240×240 square (DLsite: "main_240x240").</summary>
Square240,
/// <summary>300×300 square (DLsite: "main_300x300").</summary>
Square300,
/// <summary>Native “main” image (typically ~560×420).</summary>
Main
}

View File

@@ -40,11 +40,13 @@ else
@foreach (var item in searchResults.Items)
{
<div class="circle-item">
@* <JImage @key="item.CircleId" ContainerClass="j-circle-image-container" ImageClass="j-circle-image" Source="@ImageUrlProvider.GetImageURL(item.LatestProductId, item.LatestVoiceWorkHasImage ?? false, item.LatestVoiceWorkSalesDate, "main")" /> *@
<JImage @key="item.CircleId" ContainerClass="j-circle-image-container-2" ImageClass="j-circle-image-2" Source="@ImageUrlProvider.GetImageURL(item.LatestProductId, item.LatestVoiceWorkHasImage ?? false, item.LatestVoiceWorkSalesDate, "main")" />
<div class="fdfs">
<div class="circle-name">@item.Name</div>
<div>@item.MakerId</div>
</div>
@if (item.Favorite)
{
<MudChip T="string" Label="true" Color="Color.Info" Style="width: 100%" Variant="Variant.Outlined">Favorite</MudChip>
@@ -195,7 +197,7 @@ else
.circle-item {
display: flex;
align-items: center;
padding: 1rem;
padding: .5rem;
border-width: 2px;
border-top-color: #353B4C;
border-left-color: #212630;
@@ -204,8 +206,23 @@ else
border-radius: 30px;
background-image: linear-gradient(0deg, #1C2029, #1C1F28);
display: grid;
grid-template-columns: 1fr 100px 100px 100px 100px;
grid-column-gap: 2rem;
grid-template-columns: auto 1fr 100px 100px 100px 100px;
grid-column-gap: 1rem;
}
.j-circle-image-container-2 {
height: auto;
}
.j-circle-image-2 {
display: block;
object-fit: cover;
background-color: black;
border: 1px solid #949494;
box-sizing: border-box;
border-radius: 100%;
width: 70px;
height: 70px;
}
.circle-name {

View File

@@ -1,70 +1,84 @@
@page "/"
@inject VoiceWorksClient Client
@using JSMR.Application.VoiceWorks.Queries.Search
@using JSMR.UI.Blazor.Components
@using JSMR.UI.Blazor.Services
<PageTitle>Home</PageTitle>
<MudTabs Elevation="2" Rounded="true" ApplyEffectsToContainer="true" PanelClass="pa-6">
<MudTabPanel Text="Available" Icon="@Icons.Material.Filled.Home">
@if (availableVoiceWorks is null)
{
<p>Loading…</p>
}
else if (availableVoiceWorks.Length == 0)
{
<p>No results.</p>
}
else
{
<ul>
@foreach (var v in availableVoiceWorks)
{
<li>@v.ProductId @v.ProductName</li>
}
</ul>
}
<JProductCollection Products="availableVoiceWorks"></JProductCollection>
</MudTabPanel>
<MudTabPanel Text="Upcoming" Icon="@Icons.Material.Filled.ArrowUpward">
@if (upcomingVoiceWorks is null)
{
<p>Loading…</p>
}
else if (upcomingVoiceWorks.Length == 0)
{
<p>No results.</p>
}
else
{
<ul>
@foreach (var v in upcomingVoiceWorks)
{
<li>@v.ProductId @v.ProductName</li>
}
</ul>
}
<JProductCollection Products="upcomingVoiceWorks"></JProductCollection>
</MudTabPanel>
<MudTabPanel Text="Announcements" Icon="@Icons.Material.Filled.Home">
@if (announcedVoiceWorks is null)
{
<p>Loading…</p>
}
else if (announcedVoiceWorks.Length == 0)
{
<p>No results.</p>
}
else
{
<ul>
@foreach (var v in announcedVoiceWorks)
{
<li>@v.ProductId @v.ProductName</li>
}
</ul>
}
<JProductCollection Products="announcedVoiceWorks"></JProductCollection>
</MudTabPanel>
</MudTabs>
<style>
.j-product-items-container {
display: flex;
flex-direction: column;
gap: 2rem;
}
.j-voice-work-card {
display: flex;
gap: 1rem;
background-image: linear-gradient(0deg, rgb(30, 53, 69), rgb(39, 59, 73));
border-color: rgb(63, 78, 88);
background-image: linear-gradient(0deg, rgb(30, 53, 69), rgb(57, 79, 94));
}
.j-voice-work-image-container {
width: 240px;
width: 300px;
}
.j-voice-work-card > .j-voice-work-image-container {
flex-shrink: 0;
}
.j-voice-work-image {
border-radius: 20px;
}
.j-voice-work-content {
display: flex;
flex-direction: column;
gap: .5rem;
}
.j-voice-work-card > .j-voice-work-content {
flex-grow: 1;
}
.j-product-title {
font-size: 1.25rem;
font-weight: 600;
font-family: "Poppins", "M+ 1p";
color: #d2dce6;
text-shadow: 1px 1px 2px black;
}
.j-product-description {
/* color: #7C8099; */
font-size: 1rem;
font-family: "Poppins", "M+ 1p";
}
.j-voice-work-info {
width: 240px;
}
.j-voice-work-card > .j-voice-work-info {
flex-shrink: 0;
}
</style>
@code {
VoiceWorkSearchResult[]? availableVoiceWorks;
VoiceWorkSearchResult[]? upcomingVoiceWorks;
@@ -143,7 +157,7 @@
},
SortOptions =
[
//new(VoiceWorkSortField.Fa, Application.Common.Search.SortDirection.Ascending),
new(VoiceWorkSortField.FavoriteCircle, Application.Common.Search.SortDirection.Ascending),
new(VoiceWorkSortField.WishlistCount, Application.Common.Search.SortDirection.Descending)
]
}

View File

@@ -1,5 +1,6 @@
@page "/voiceworks"
@using JSMR.Application.VoiceWorks.Queries.Search
@using JSMR.UI.Blazor.Components
@using JSMR.UI.Blazor.Services
@inject VoiceWorksClient Client
@@ -7,26 +8,10 @@
<h3>VoiceWorks</h3>
@if (items is null)
{
<p>Loading…</p>
}
else if (items.Count == 0)
{
<p>No results.</p>
}
else
{
<ul>
@foreach (var v in items)
{
<li>@v.ProductId @v.ProductName</li>
}
</ul>
}
<JProductCollection Products="items"></JProductCollection>
@code {
List<VoiceWorkSearchResult>? items;
VoiceWorkSearchResult[]? items;
protected override async Task OnInitializedAsync()
{
@@ -36,12 +21,6 @@ else
var result = await Client.SearchAsync(request);
// if (result.Ok)
// {
// items = result.Value!.Results.Items.ToList();
// }
//items = result.Value?.Results.Items ?? [];
items = result.Results.Items.ToList();
items = result?.Results.Items ?? [];
}
}

View File

@@ -41,19 +41,15 @@ public static class ImageUrlProvider
var imageUrlTemplate = "//img.dlsite.jp/[folder]/images2/[imageType1]/[imageWorkType]/[fullRoundedProductId]/[productId][imageType2]_img_[imageSize].jpg";
var productIdWithoutPrefixString = productId.Substring(2);
string productIdWithoutPrefixString = productId.Substring(2);
int productIdWithoutPrefix = Convert.ToInt32(productId.Substring(2));
string productIdPrefix = productId.Substring(0, 2);
double something = (double)((productIdWithoutPrefix / 1000) * 1000);
int roundedProductId = (int)Math.Round(Math.Ceiling((double)productIdWithoutPrefix / 1000) * 1000);
//string actualRoundedProductId = ("000000" + roundedProductId.ToString()).Substring(roundedProductId.ToString().Length);
//string fullRoundedProductId = productIdPrefix + actualRoundedProductId;
var productIdWithPrefixStringLength = productIdWithoutPrefixString.Length;
var zeroPadLength = productIdWithPrefixStringLength - roundedProductId.ToString().Length;
int productIdWithPrefixStringLength = productIdWithoutPrefixString.Length;
int zeroPadLength = productIdWithPrefixStringLength - roundedProductId.ToString().Length;
var fullRoundedProductId = productIdPrefix.PadRight(productIdPrefix.Length + zeroPadLength, '0') + roundedProductId;

View File

@@ -17,7 +17,7 @@ a, .btn-link {
}
.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}
.content {
@@ -304,4 +304,100 @@ code {
.star_rating.mini.star_45::before {
background-position: 0 -16px;
}
/* Card */
.j-card {
padding-top: var(--card-padding-top);
padding-bottom: var(--card-padding-bottom);
padding-left: var(--card-padding-left);
padding-right: var(--card-padding-right);
border-width: var(--card-border-width);
border-top-color: var(--card-border-top-color);
border-left-color: var(--card-border-left-color);
border-right-color: var(--card-border-right-color);
border-bottom-color: var(--card-border-bottom-color);
border-radius: var(--card-border-radius);
background-image: var(--card-background-image);
}
/* Image */
.j-image-container {
}
.j-image {
width: 100%;
}
/* Product */
.j-product-items-container {
display: flex;
flex-direction: column;
gap: 2rem;
}
.j-voice-work-card {
display: flex;
gap: 1rem;
background-image: linear-gradient(0deg, rgb(30, 53, 69), rgb(39, 59, 73));
border-color: rgb(63, 78, 88);
background-image: linear-gradient(0deg, rgb(30, 53, 69), rgb(57, 79, 94));
}
.j-voice-work-image-container {
width: 240px;
width: 300px;
}
.j-voice-work-card > .j-voice-work-image-container {
flex-shrink: 0;
}
.j-voice-work-image {
border-radius: 20px;
}
.j-voice-work-content {
display: flex;
flex-direction: column;
gap: .5rem;
}
.j-voice-work-card > .j-voice-work-content {
flex-grow: 1;
}
.j-product-title {
font-size: 1.25rem;
font-weight: 600;
font-family: "Poppins", "M+ 1p";
color: #d2dce6;
text-shadow: 1px 1px 2px black;
}
.j-product-description {
/* color: #7C8099; */
font-size: 1rem;
font-family: "Poppins", "M+ 1p";
}
.j-voice-work-card > .j-voice-work-content > .j-product-description {
flex-grow: 1;
}
.j-voice-work-info {
width: 240px;
}
.j-voice-work-card > .j-voice-work-info {
flex-shrink: 0;
}
/* Tags */
.j-tags {
display: flex;
gap: 1rem;
flex-wrap: wrap;
}

View File

@@ -1,7 +1,7 @@
:root {
--font-family: 'Poppins';
--background-color: #131419;
--background-color-original: rgb(16, 36, 50);
--background-color-backup: #131419;
--background-color: rgb(16, 36, 50);
--input-background-color: rgb(0,20,34);
--input-border-color: #304562;
--primary-text-color: rgb(180,200, 214);
@@ -22,4 +22,26 @@
--product-footer-text-color: rgb(220,230,234);
--expected-date-text-color: #ffe073;
--planned-date-text-color: #73bdff;
--card-padding-top: .5rem;
--card-padding-bottom: .5rem;
--card-padding-left: .5rem;
--card-padding-right: .5rem;
--card-background-image: linear-gradient(0deg, #1C2029, #1C1F28);
--card-border-radius: 30px;
--card-border-width: 2px;
--card-border-top-color: #353B4C;
--card-border-left-color: #212630;
--card-border-right-color: #212531;
--card-border-bottom-color: #212530;
}
/*
padding: .5rem;
border-width: 2px;
border-top-color: #353B4C;
border-left-color: #212630;
border-right-color: #212531;
border-bottom-color: #212530;
border-radius: 30px;
background-image: linear-gradient(0deg, #1C2029, #1C1F28);
*/

View File

@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36408.4
# Visual Studio Version 18
VisualStudioVersion = 18.0.11205.157 d18.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JSMR.Domain", "JSMR.Domain\JSMR.Domain.csproj", "{BC16F228-63B0-4EE6-9B96-19A38A31C125}"
EndProject