Printing tracking for owned copies and deck art, and an outdated-card-data notice
Owned copies can now be pinned to a printing + finish (nonfoil / foil / etched), activating the long-dormant library_card_copies table as groups of identical copies. library_cards.quantity_owned stays authoritative: groups are a subset of it, and the rest are "unspecified". Tracking more copies than are unspecified grows ownership to fit; lowering ownership trims the most recently added groups first. Migration 0006 adds the group quantity, a unique (card, printing, finish) index, and deck_cards.preferred_scryfall_id. A deck slot can show a chosen printing's art, both faces from that printing, falling back to the default when the id no longer resolves. It survives quantity/board changes and is kept by duplicate_deck. UI: the click preview's info tile gets a Printings section when opened from the Library or a deck (copy groups with steppers, "Add a printing…", and "Shown in this deck" with Change…/Default), backed by a printing picker grid. The picker takes Escape in the capture phase so it closes without closing the preview under it. Sync: CardEntry.printings (keyed "<scryfall_id>/<finish>") and DeckCardEntry.printing. Cards now merge field by field with groups per key, so a quantity edit and a printing added on another device both survive; merged groups are clamped deterministically. A document is written as version 2 only once it holds printing data, so older builds and servers refuse it instead of dropping the data and syncing the loss. Decklists: "(SET) NUM", optionally "*F*"/"*E*", round-trips through deck and library export/import. Library export writes a line per group plus one for unspecified copies. reference.db printings gain lang and released_at for picker labels and ordering. When the installed card data lacks any column this build's reference schema has (found by diffing against the schema applied in-memory, so no list to maintain), a notice under the top bar offers Update card data (and Import card data… on phones); "Not now" is remembered until something more goes missing.
This commit is contained in:
56
src/api.ts
56
src/api.ts
@@ -36,6 +36,32 @@ export interface DeckEntry extends CardRef {
|
||||
* analysis stats and library assignment totals. */
|
||||
is_maybeboard: boolean;
|
||||
quantity_owned: number;
|
||||
/** The printing whose art this slot shows; `image_uri` already reflects it. */
|
||||
preferred_scryfall_id: string | null;
|
||||
}
|
||||
|
||||
/** One printing of a card, for the printing picker and copy labels. */
|
||||
export interface PrintingRef {
|
||||
scryfall_id: string;
|
||||
set_code: string; // lowercase, e.g. "2x2"
|
||||
collector_number: string;
|
||||
lang: string; // "" on an older card database
|
||||
finishes: string[]; // "nonfoil" | "foil" | "etched"
|
||||
rarity: string;
|
||||
image_uri: string;
|
||||
back_image_uri: string;
|
||||
price_usd: number;
|
||||
released_at: string; // YYYY-MM-DD, "" on an older card database
|
||||
}
|
||||
|
||||
/** Owned copies pinned to one printing + finish. A subset of the card's
|
||||
* `quantity_owned`; copies in no group are unspecified. */
|
||||
export interface LibraryCopy {
|
||||
scryfall_id: string;
|
||||
finish: string;
|
||||
quantity: number;
|
||||
/** Null when the printing has left the card database. */
|
||||
printing: PrintingRef | null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,8 +74,21 @@ export interface DeckEntry extends CardRef {
|
||||
export type PreviewCard = CardRef & {
|
||||
quantity_owned?: number;
|
||||
decks?: string[];
|
||||
preferred_scryfall_id?: string | null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Where a click preview was opened from, when the caller lets it edit
|
||||
* printings: the info tile then tracks owned copies by printing, and — for a
|
||||
* deck slot — chooses which printing's art the slot shows.
|
||||
*/
|
||||
export interface PreviewContext {
|
||||
/** Called after any printing change, so the caller can refetch. */
|
||||
onChanged: () => void;
|
||||
/** The deck the previewed card is a slot of. */
|
||||
deckId?: number;
|
||||
}
|
||||
|
||||
export interface Deck {
|
||||
id: number;
|
||||
name: string;
|
||||
@@ -149,6 +188,10 @@ export interface Status {
|
||||
reference_ready: boolean;
|
||||
card_count: number;
|
||||
reference_updated_at: number | null; // unix seconds
|
||||
/** Columns the installed card data lacks ("table.column") because an older
|
||||
* version built it. Non-empty means some features are degraded until the
|
||||
* user updates card data. */
|
||||
reference_missing_columns: string[];
|
||||
/** True on mobile builds. Used to hide affordances that depend on choosing an
|
||||
* arbitrary filesystem path for library.db — Android scoped storage has no
|
||||
* equivalent, and SQLite can't be backed by a content:// URI. */
|
||||
@@ -407,6 +450,19 @@ export const api = {
|
||||
invoke<CardSuggestion[]>("suggest_cards", { deckId, offset, options }),
|
||||
cardDecks: (oracleId: string) => invoke<Deck[]>("card_decks", { oracleId }),
|
||||
|
||||
/** Every paper printing of a card, newest first. */
|
||||
listPrintings: (oracleId: string) => invoke<PrintingRef[]>("list_printings", { oracleId }),
|
||||
listLibraryCopies: (oracleId: string) =>
|
||||
invoke<LibraryCopy[]>("list_library_copies", { oracleId }),
|
||||
/** Set how many owned copies are this printing + finish (0 removes the
|
||||
* group). Resolves with the card's owned quantity, which grows to fit. */
|
||||
setLibraryCopy: (oracleId: string, scryfallId: string, finish: string, quantity: number) =>
|
||||
invoke<number>("set_library_copy", { oracleId, scryfallId, finish, quantity }),
|
||||
/** Choose which printing a deck slot shows; null restores the default.
|
||||
* Resolves with the slot afterwards (its art reflecting the choice). */
|
||||
setDeckCardPrinting: (deckId: number, oracleId: string, scryfallId: string | null) =>
|
||||
invoke<DeckEntry | null>("set_deck_card_printing", { deckId, oracleId, scryfallId }),
|
||||
|
||||
exportDeck: (deckId: number, path: string) => invoke<void>("export_deck", { deckId, path }),
|
||||
importDeck: (path: string, name: string) =>
|
||||
invoke<DeckImportResult>("import_deck", { path, name }),
|
||||
|
||||
Reference in New Issue
Block a user