feat: filter the library by variant kind from the smart filter bar #103
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/library-variant-kind-filter"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The backend has supported filtering by variant kind since the search feature shipped; the library had no way to ask for it. Now typing
kind:translated(autocompleted fromkinonward) shows only doujins that have a variant of that kind, combined with every other token, as a chip that reads "Kind: Translated" and deletes like any other.What's in
kind:prefix, suggestions from the enum, the chip's label, colour and icon, and the query builder sending the backend's enum name (Translated,Reedition) — the integer the entities serialise would be a 400.kind:translation) or a negated one (-kind:translated) still commits nothing — that is deliberate — but the bar no longer swallows the Enter press silently. One hint line under the field names the four accepted kinds and says a kind cannot be excluded; it clears as soon as the text parses.Verification
scripts/verify.sh app: green, 788 tests (24 new across token parsing, suggestions, the reducer's replace rule, the query builder, and two widget tests).scripts/verify.sh backend: green, unchanged — nothing backend-side was touched; the wire names were confirmed against the search endpoint's defined-value parse and its existing tests.Decisions to revisit
Only the four enum names are accepted, with punctuation ignored so
kind:re-editionworks; friendly synonyms would go in one place if you want them. The kind lives in the token list rather than as a state field, which is enough while the filter bar is the only entry point — a dropdown or the assistant setting it would be the moment to promote it. A review finding also removed an unreachable guard in the kind parser.🤖 Generated with Claude Code
Review follow-ups on the variant-kind filter token. What/why: - `parseVariantKind` drops its `if (normalized.isEmpty) return null` guard: no kind's wire name normalizes to the empty string, so the loop below already returns null for an empty or all-punctuation input. The doc comment now says why the guard is unnecessary; the behaviour stays pinned by `parseVariantKind('')` in variant_display_test. - Typing `kind:translation` or `-kind:translated` commits nothing on purpose (unknown kind word / no exclude form for a single-valued server filter), but Enter then looked broken: no chip, no change, the words still sitting in the field. The bar now shows one hint line under the input naming the four kinds and saying a kind cannot be excluded; any edit retires it. Smallest signal that fits the widget — a Text in the column it already builds, no dialog, no snackbar, no store state. Verified: `scripts/verify.sh app` green (788 tests, +2; analyze 0 issues). Mutation probes went red as expected: disabling the hint branch (both new tests), removing the clear-on-edit (the "hint gone" arm), dropping the `-` strip in the predicate (the negated-kind test), and turning the parser's wire-name comparison into `startsWith` (the empty-input case that used to sit behind the deleted guard). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>Flutter Coverage
Total: 79.9% (7694 of 9631)
🔮 fufu~ Jibril reviewed your code!
Oh? Oh! A filter token done properly~ The wire vocabulary lives in exactly one place, the parser drops what it can't honour instead of inventing lies, and a dead Enter press gets an explanation instead of silence. I traced every layer against the backend source and I'm delightfully cornered — nothing to dissect~ ♡
Verdict: ✅ Looks good to me~
What I actually verified (not just read!):
SearchEndpoints.cs:46-54parsesdto.VariantKindthroughDoujinEndpoints.TryParseDefinedEnum—Enum.TryParse(ignoreCase: true)+Enum.IsDefinedover the C#VariantKind { Original, Translated, Reedition, Other }(ApplicationCore/Enums/VariantKind.cs). SovariantKindWireNameemittingReedition(not the labelRe-edition) is exactly right, and the punctuated-spelling acceptance (kind:re-edition) via_normalizeKindis a genuinely elegant little touch — one normalizer serves parse, suggest, and the chip-label fallback.library_reducer.dart:196), other tokens untouched — honest single-value semantics instead of stacked chips promising an OR the server can't honour. Mutation-probed: removed thekeptfilter → exactly the 2 replace-arm tests went red; restored → green.-kind:…returning null instead of falling through to the stray negative-tag fallback. Mutation-probed: deleted the guard →a negated kind commits nothingred; restored → green.variantKind: _variantKindFilter(lib.tokens)rides both the fresh search and load-more paging. Mutation-probed: hardcoded null → bothcarries the variant kindandpaging … keeps the variant kindred; restored → green.else ifbranch → bothan unknown kind word explains itselfanda negated kind gets the same hintred; restored → green. The clear-on-edit arm is pinned too._applyTokensbreaks even onvariantKindand the epic reads the token list directly — a deviation from the legacy-mirror pattern, yes, but documented in both places and safe: the reducer guarantees at most one kind token, so the epic's first-match scan can't lie.SetFilterTokensActionhas no production callers that could smuggle in a duplicate (grep: tests only). This is the right amount of architecture~--concurrency=1— the sandbox is being squeezed by two sibling review sessions, so parallel DartWorkers were dying, not your code),flutter analyze0 issues, CI coverage bot green for head6abd446withvariant_display.dart100% (22/22) andlibrary_reducer.dart100% (122/122). The 92.2% onfilter_token.dartis the pre-existing character/series suggestion arms, untouched by this PR.VariantKind?at line 53 of the ADR).💡 Little ideas (non-blocking)~
_kVariantKindHinthand-enumerates the four kinds in prose. The label and wire maps are compiler-forced switches over the enum, but the hint string isn't: a hypothetical fifth kind would leave the hint silently stale. Deriving the list fromvariantKindsMatching('')(it already lives next to the vocabulary it owns) would make all three drift-proof the same way. Purely a future-proofing nicety~_looksLikeVariantKindInputmirrors the parser's two drop points by hand. At two cases with both sides mutation-pinned this is fine; if the parser ever grows a third silent drop, give the predicate a twin then, not now.✅ What I liked~
if (normalized.isEmpty) return nullguard — and the commit message proving why it was unreachable, with thestartsWithmutant red onparseVariantKind(''). Dead code removed with evidence, not vibes~kin) so a barekstill belongs to the entity matches, and kinds-first ordering pinned against a tag literally namedkindness. Someone thought about the crowding problem~Kind: Omnibus) — defensive without being paranoid, and tested.Fufu~ ship it. ♡
Automated review by Jibril · 2026-09-06
CI/CD: passed for head SHA
6abd446(coverage bot, full app suite green) · Local checks: analyze 0 issues · 129/129 across the 5 touched suites · 4/4 mutation probes red