Card filter: show only Universes Beyond cards
The Universes Beyond toggle now cycles off → hide → only, backed by a new only_universes_beyond library filter. Choosing "only" counts as a real query, so Card Search runs it on its own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -53,6 +53,9 @@ fn push_card_filters(
|
||||
if filter.exclude_universes_beyond {
|
||||
sql.push_str(" AND oc.universes_beyond = 0");
|
||||
}
|
||||
if filter.only_universes_beyond {
|
||||
sql.push_str(" AND oc.universes_beyond = 1");
|
||||
}
|
||||
if filter.exclude_digital_only {
|
||||
sql.push_str(" AND oc.digital_only = 0");
|
||||
}
|
||||
@@ -387,7 +390,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn excluding_universes_beyond_hides_flagged_cards_only_when_asked() {
|
||||
fn universes_beyond_filters_hide_or_keep_only_flagged_cards() {
|
||||
let fx = owned_fixture();
|
||||
// Stand in for what the ingest derives: a card that exists only as
|
||||
// Universes Beyond.
|
||||
@@ -410,6 +413,16 @@ mod tests {
|
||||
// Off unless asked for — this must not quietly change existing searches.
|
||||
let all = names(&fx.catalog.search_library(&LibraryFilter::default()).unwrap());
|
||||
assert!(all.contains(&"Serra Angel".to_string()));
|
||||
|
||||
let f = LibraryFilter {
|
||||
only_universes_beyond: true,
|
||||
..Default::default()
|
||||
};
|
||||
assert_eq!(
|
||||
names(&fx.catalog.search_library(&f).unwrap()),
|
||||
vec!["Serra Angel"],
|
||||
"only the flagged card is kept"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -421,6 +421,10 @@ pub struct LibraryFilter {
|
||||
/// existed, the same way the other reference-schema additions degrade.
|
||||
#[serde(default)]
|
||||
pub exclude_universes_beyond: bool,
|
||||
/// The opposite: only cards that exist only as Universes Beyond. Inert
|
||||
/// against an older `reference.db` in the same way.
|
||||
#[serde(default)]
|
||||
pub only_universes_beyond: bool,
|
||||
/// Hide cards that exist only on Arena/MTGO (Alchemy rebalances and the
|
||||
/// like) and so can never be in a physical collection.
|
||||
#[serde(default)]
|
||||
|
||||
@@ -201,6 +201,8 @@ export interface LibraryFilter {
|
||||
* Warhammer 40,000, Marvel, …). Ordinary cards reprinted into a UB set are
|
||||
* kept. Needs card data downloaded after this shipped; inert otherwise. */
|
||||
exclude_universes_beyond?: boolean;
|
||||
/** The opposite: only cards that exist only as Universes Beyond. */
|
||||
only_universes_beyond?: boolean;
|
||||
/** Hide Arena/MTGO-only cards (Alchemy rebalances) — they can't be owned on
|
||||
* paper. Needs card data downloaded after this shipped; inert otherwise. */
|
||||
exclude_digital_only?: boolean;
|
||||
|
||||
@@ -2391,6 +2391,8 @@ const ICON_PACKAGE = icon(
|
||||
*/
|
||||
export type CardCriteria = {
|
||||
excludeUb: boolean;
|
||||
/** Never set together with `excludeUb`: the toggle cycles off → hide → only. */
|
||||
onlyUb: boolean;
|
||||
excludeDigital: boolean;
|
||||
excludeFunny: boolean;
|
||||
priceMin: string;
|
||||
@@ -2410,6 +2412,7 @@ export type CardCriteria = {
|
||||
|
||||
export const EMPTY_CRITERIA: CardCriteria = {
|
||||
excludeUb: false,
|
||||
onlyUb: false,
|
||||
excludeDigital: false,
|
||||
excludeFunny: false,
|
||||
priceMin: "",
|
||||
@@ -2451,6 +2454,7 @@ const num = (s: string): number | null => (s.trim() === "" ? null : Number(s));
|
||||
export function applyCardCriteria(c: CardCriteria): Partial<LibraryFilter> {
|
||||
return {
|
||||
exclude_universes_beyond: c.excludeUb,
|
||||
only_universes_beyond: c.onlyUb,
|
||||
exclude_digital_only: c.excludeDigital,
|
||||
exclude_funny: c.excludeFunny,
|
||||
price_min: num(c.priceMin),
|
||||
@@ -2474,10 +2478,12 @@ export function applyCardCriteria(c: CardCriteria): Partial<LibraryFilter> {
|
||||
* The three "exclude" toggles deliberately don't count: they refine a result
|
||||
* set rather than select one, so ticking "hide Un-sets" on its own shouldn't
|
||||
* make Card Search pull every card in the database. Choosing a format, a
|
||||
* power range, or "not owned" is a real query, and does.
|
||||
* power range, "not owned" or "only Universes Beyond" is a real query, and
|
||||
* does.
|
||||
*/
|
||||
export function criteriaActive(c: CardCriteria): boolean {
|
||||
return (
|
||||
c.onlyUb ||
|
||||
c.canBeCommander ||
|
||||
c.vanillaOnly ||
|
||||
c.notOwned ||
|
||||
@@ -2611,13 +2617,29 @@ export function CardCriteriaFields({
|
||||
<div className="icon-toggles">
|
||||
{/* Exclusions read as "no X" — tinted with the danger colour when
|
||||
active, so removing things looks different from selecting them. */}
|
||||
{/* Three states: off → hide → only → off. "Only" looks like the
|
||||
other "only" toggles rather than an exclusion. */}
|
||||
<IconToggle
|
||||
icon={ICON_CROSSOVER}
|
||||
label="Hide Universes Beyond"
|
||||
hint="Cards that only exist as another property — Lord of the Rings, Warhammer 40,000, Marvel. Normal cards reprinted in those sets stay."
|
||||
exclude
|
||||
on={c.excludeUb}
|
||||
onToggle={() => onChange({ excludeUb: !c.excludeUb })}
|
||||
label={
|
||||
c.onlyUb
|
||||
? "Only Universes Beyond (click to show all)"
|
||||
: c.excludeUb
|
||||
? "Hiding Universes Beyond (click for only them)"
|
||||
: "Hide Universes Beyond"
|
||||
}
|
||||
hint="Cards that only exist as another property — Lord of the Rings, Warhammer 40,000, Marvel. Normal cards reprinted in those sets count as normal cards. Click again to show only these cards, and once more to show everything."
|
||||
exclude={!c.onlyUb}
|
||||
on={c.excludeUb || c.onlyUb}
|
||||
onToggle={() =>
|
||||
onChange(
|
||||
c.onlyUb
|
||||
? { excludeUb: false, onlyUb: false }
|
||||
: c.excludeUb
|
||||
? { excludeUb: false, onlyUb: true }
|
||||
: { excludeUb: true, onlyUb: false },
|
||||
)
|
||||
}
|
||||
/>
|
||||
<IconToggle
|
||||
icon={ICON_SCREEN}
|
||||
|
||||
Reference in New Issue
Block a user