Decks tab: a deck list page, with the builder on its own screen
The deck list is no longer a sidebar pinned beside the builder; on every screen size the Decks tab opens to a deck list page (DeckList), and picking a deck opens its builder full-width with an "All decks" back button. The list shows each deck as an art tile (or a compact row, toggleable) with its colors, size against its format, owned %, cost and legality, and can be searched, filtered by format and sorted (recently opened, name, format, size, cost, owned %). Rename/duplicate/export/delete live in each deck's menu and beside the builder's title; New deck now asks for a format too, defaulting to the last one used. A deck's cover art is the commander, else its priciest nonland, unless a card is chosen with "Use as cover" in the card preview. The cover is stored in decks.cover_oracle_id (migration 0007) and synced as DeckDoc.cover; a document carrying one is version 3, so the sync server must be updated before clients publish covers. The list's stats come from one all_deck_contents query, summarised with the builder's own analysis code, now in deckAnalysis.ts. Closes #9 Closes #11 Closes #12
This commit is contained in:
@@ -95,6 +95,8 @@ export interface PreviewContext {
|
||||
onChanged: () => void;
|
||||
/** The deck the previewed card is a slot of. */
|
||||
deckId?: number;
|
||||
/** That deck's chosen cover card, so the preview can offer to change it. */
|
||||
coverOracleId?: string | null;
|
||||
}
|
||||
|
||||
export interface Deck {
|
||||
@@ -103,6 +105,8 @@ export interface Deck {
|
||||
format: string;
|
||||
commander_oracle_id: string | null;
|
||||
notes: string;
|
||||
/** The card chosen to headline the deck in the deck list; null = automatic. */
|
||||
cover_oracle_id: string | null;
|
||||
}
|
||||
|
||||
export interface DeckImportResult {
|
||||
@@ -493,6 +497,9 @@ export const api = {
|
||||
invoke<Deck>("duplicate_deck", { deckId, newName }),
|
||||
setDeckFormat: (deckId: number, format: string) =>
|
||||
invoke<void>("set_deck_format", { deckId, format }),
|
||||
/** Choose the card headlining the deck in the deck list; null = automatic. */
|
||||
setDeckCover: (deckId: number, oracleId: string | null) =>
|
||||
invoke<void>("set_deck_cover", { deckId, oracleId }),
|
||||
setDeckCommander: (deckId: number, oracleId: string | null) =>
|
||||
invoke<void>("set_deck_commander", { deckId, oracleId }),
|
||||
/** One commander, or a partner pair; the first is the deck's primary. */
|
||||
@@ -512,6 +519,8 @@ export const api = {
|
||||
shareCardImage: (url: string, name: string) =>
|
||||
invoke<"copied" | "shared">("share_card_image", { url, name }),
|
||||
deckContents: (deckId: number) => invoke<DeckEntry[]>("deck_contents", { deckId }),
|
||||
/** Every deck's contents by deck id; decks with no cards are absent. */
|
||||
allDeckContents: () => invoke<Record<number, DeckEntry[]>>("all_deck_contents"),
|
||||
suggestCards: (deckId: number, offset = 0, options: { unusual?: boolean; owned_only?: boolean } = {}) =>
|
||||
invoke<CardSuggestion[]>("suggest_cards", { deckId, offset, options }),
|
||||
cardDecks: (oracleId: string) => invoke<Deck[]>("card_decks", { oracleId }),
|
||||
|
||||
Reference in New Issue
Block a user