Added tag filter state persistence.
All checks were successful
ci / build-test (push) Successful in 2m26s
ci / publish-image (push) Successful in 1m32s

This commit is contained in:
2026-04-26 23:39:47 -04:00
parent 2355d7fe65
commit 77a02a543d
3 changed files with 197 additions and 101 deletions

View File

@@ -0,0 +1,109 @@
using JSMR.Application.Common.Search;
using JSMR.Application.Tags.Queries.Search;
using JSMR.Application.Tags.Queries.Search.Contracts;
using Microsoft.AspNetCore.WebUtilities;
using System.Globalization;
namespace JSMR.UI.Blazor.Filters;
public sealed record TagFilterState : IFilterState<SearchTagsRequest>
{
public string? Keywords { get; init; }
public int PageNumber { get; init; } = 1;
public int PageSize { get; init; } = 100;
public IReadOnlyList<SortOption<TagSortField>> SortOptions { get; init; } =
[
new(TagSortField.Name, SortDirection.Ascending)
];
public QueryParameters ToQuery()
{
QueryParameters query = [];
query.Set("keywords", Keywords);
if (PageNumber != 1)
query.Set("pageNumber", PageNumber.ToString(CultureInfo.InvariantCulture));
if (PageSize != 100)
query.Set("pageSize", PageSize.ToString(CultureInfo.InvariantCulture));
if (!IsDefaultSort(SortOptions))
{
query.Set("sort", string.Join(",", SortOptions.Select(x =>
$"{x.Field}:{ToQueryDirection(x.Direction)}")));
}
return query;
}
public static TagFilterState FromQuery(string query)
{
QueryParameters queryParameters = new(QueryHelpers.ParseQuery(query));
return new TagFilterState
{
Keywords = queryParameters.GetValue("keywords"),
PageNumber = Math.Max(1, queryParameters.GetInteger("pageNumber", 1)),
PageSize = queryParameters.GetInteger("pageSize", 100),
SortOptions = ParseSortOptions(queryParameters.GetValue("sort"))
};
}
public SearchTagsRequest ToSearchRequest()
{
return new(
Options: new()
{
PageNumber = PageNumber,
PageSize = PageSize,
Criteria = new()
{
Name = Keywords
},
SortOptions = [.. SortOptions]
}
);
}
private static IReadOnlyList<SortOption<TagSortField>> ParseSortOptions(string? value)
{
if (string.IsNullOrWhiteSpace(value))
return DefaultSort();
var result = new List<SortOption<TagSortField>>();
foreach (var part in value.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
{
var pieces = part.Split(':', 2, StringSplitOptions.TrimEntries);
if (pieces.Length != 2)
continue;
if (!Enum.TryParse<TagSortField>(pieces[0], ignoreCase: true, out var field))
continue;
var direction = pieces[1].Equals("desc", StringComparison.OrdinalIgnoreCase)
? Application.Common.Search.SortDirection.Descending
: Application.Common.Search.SortDirection.Ascending;
result.Add(new(field, direction));
}
return result.Count > 0 ? result : DefaultSort();
}
private static IReadOnlyList<SortOption<TagSortField>> DefaultSort() =>
[
new(TagSortField.Name, Application.Common.Search.SortDirection.Ascending)
];
private static string ToQueryDirection(Application.Common.Search.SortDirection direction)
=> direction == Application.Common.Search.SortDirection.Descending ? "desc" : "asc";
private static bool IsDefaultSort(IReadOnlyList<SortOption<TagSortField>> sortOptions)
=> sortOptions.Count == 1
&& sortOptions[0].Field == TagSortField.Name
&& sortOptions[0].Direction == Application.Common.Search.SortDirection.Ascending;
}