Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions client/src/App.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import {
} from "./helper";
import { eraseCookie, getCookie, setCookie } from "./cookies";
import { ButtonColor, Alert, fireToast } from "./alerts";
import { replaceAltArts } from "./altArtDisplay";
import parseCSV from "../../src/parseCSV";

import type CardPool from "./components/CardPool.vue";
Expand Down Expand Up @@ -151,6 +152,7 @@ const defaultSettings = {
displayBotScores: false,
fixedDeck: false,
foilEffect: true,
replaceAltArts: false,
pickOnDblclick: true,
enableSound: true,
enableNotifications: false,
Expand All @@ -162,6 +164,7 @@ const defaultSettings = {
};
const storedSettings = JSON.parse(localStorage.getItem(localStorageSettingsKey) ?? "{}");
const initialSettings: typeof defaultSettings = Object.assign({ ...defaultSettings }, storedSettings);
replaceAltArts.value = initialSettings.replaceAltArts;

export type SessionUser = {
userID: string;
Expand Down Expand Up @@ -438,6 +441,7 @@ export default defineComponent({
displayBotScores: initialSettings.displayBotScores,
fixedDeck: initialSettings.fixedDeck,
foilEffect: initialSettings.foilEffect,
replaceAltArts: initialSettings.replaceAltArts,

fixedDeckState: {
ht: 400,
Expand Down Expand Up @@ -4285,6 +4289,10 @@ export default defineComponent({
foilEffect() {
this.storeSettings();
},
replaceAltArts() {
replaceAltArts.value = this.replaceAltArts;
this.storeSettings();
},
fixedDeck() {
this.storeSettings();
},
Expand Down
14 changes: 14 additions & 0 deletions client/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,20 @@
</button>
</div>
<span class="personal-settings">
<div
class="clickable personal-settings-icon"
:class="{ faded: !replaceAltArts, crossed: !replaceAltArts }"
@click="replaceAltArts = !replaceAltArts"
v-tooltip="{
content: `Replace Alt. Arts: <strong>${replaceAltArts ? 'Enabled' : 'Disabled'}</strong><br />
<small>Use the default art for showcase and borderless cards.</small><br />
<small>Only changes what you see, other players are not affected.</small>`,
html: true,
}"
tabindex="0"
>
<font-awesome-icon icon="fa-solid fa-palette" style="font-size: 1.2em; vertical-align: -20%" />
</div>
<div
class="clickable personal-settings-icon"
:class="{ faded: !foilEffect, crossed: !foilEffect }"
Expand Down
156 changes: 156 additions & 0 deletions client/src/Scryfall.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import { CardColor, OracleID } from "@/CardTypes";

export const MaxCardsPerRequest = 75; // Scryfall API accepts requests for maximum 75 cards at once.

export type ScryfallRelatedCard = {
id: string;
object: "related_card";
component: string;
name: string;
type_line: string;
uri: string;
};

export function isScryfallRelatedCard(obj: unknown): obj is ScryfallRelatedCard {
return (
obj !== undefined && obj !== null && typeof obj === "object" && "object" in obj && obj.object === "related_card"
);
}

export type ImageURIs = {
thumb?: string;
grid?: string;
display?: string;
art?: string;
crop?: string;
// Deprecated
small?: string;
normal?: string;
large?: string;
png?: string;
art_crop?: string;
border_crop?: string;
jpeg?: string;
};

export type ScryfallCardFace = {
artist?: string;
cmc?: number;
color_indicator?: CardColor[];
colors?: CardColor[];
flavor_text?: string;
illustration_id?: string;
image_uris?: ImageURIs;
layout?: string;
loyalty?: string;
mana_cost: string;
name: string;
object: "card_face";
oracle_id?: OracleID;
oracle_text?: string;
power?: string;
printed_name?: string;
printed_text?: string;
printed_type_line?: string;
toughness?: string;
type_line?: string;
watermark?: string;
};

export function isScryfallCardFace(obj: unknown): obj is ScryfallCardFace {
return (
obj !== undefined && obj !== null && typeof obj === "object" && "object" in obj && obj.object === "card_face"
);
}

export type ScryfallCard = {
arena_id?: number;
id: string;
lang: string;
mtgo_id?: number;
mtgo_foil_id?: number;
multiverse_ids?: number[];
tcgplayer_id?: number;
tcgplayer_etched_id?: number;
cardmarket_id?: number;
object: "card";
oracle_id: string;
prints_search_uri: string;
rulings_uri: string;
scryfall_uri: string;
uri: string;

all_parts: ScryfallRelatedCard[];
card_faces: ScryfallCardFace[];
cmc: number;
color_identity: CardColor[];
color_indicator?: CardColor[];
colors?: CardColor[];
edhrec_rank?: number;
hand_modifier?: string;
keywords: string[];
layout: string;
legalities: { [format: string]: "legal" | "not_legal" | "restricted" | "banned" };
life_modifier?: string;
loyalty?: string;
mana_cost?: string;
name: string;
oracle_text?: string;
oversized: boolean;
penny_rank?: number;
power?: string;
produced_mana?: CardColor[];
reserved?: boolean;
toughness?: string;
type_line: string;

artist?: string;
attraction_lights: [];
booster: boolean;
border_color: "black" | "white" | "borderless" | "silver" | "gold";
card_back_id: string;
collector_number: string;
content_warning?: boolean;
digital: boolean;
finishes: ("foil" | "nonfoil" | "etched")[];
flavor_name?: string;
flavor_text?: string;
frame_effects?: string[];
frame: string;
full_art: boolean;
games: string[];
highres_image: boolean;
illustration_id?: string;
image_status: string;
image_uris?: ImageURIs;
prices: { [currency: string]: string };
printed_name?: string;
printed_text?: string;
printed_type_line?: string;
promo: boolean;
promo_types: string[];
purchase_uris: { [marketplace: string]: string };
rarity: "common" | "uncommon" | "rare" | "special" | "mythic" | "bonus";
related_uris: { [type: string]: string };
released_at: string;
reprint: boolean;
scryfall_set_uri: string;
set_name: string;
set_search_uri: string;
set_type: string;
set_uri: string;
set: string;
story_spotlight: boolean;
textless: boolean;
variation: boolean;
variation_of?: string;
security_stamp?: string;
watermark?: string;
"preview.previewed_at": string;
"preview.source_uri": string;
"preview.source": string;
};

export function isScryfallCard(obj: unknown): obj is ScryfallCard {
return obj !== undefined && obj !== null && typeof obj === "object" && "object" in obj && obj.object === "card";
}
34 changes: 34 additions & 0 deletions client/src/altArtDisplay.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { ref } from "vue";
import { Card } from "@/CardTypes";
import { getDefaultPrint } from "./defaultPrintCache";

type ImageURIs = Card["image_uris"];

export const replaceAltArts = ref(false);

export const displayedFrontImageURIs = (card: Card): ImageURIs => {
const defaultPrint = defaultPrintToDisplay(card);
if (!defaultPrint) {
return card.image_uris;
}
return { en: defaultPrint.image };
};

export const displayedBackImageURIs = (card: Card): ImageURIs | undefined => {
const defaultPrint = defaultPrintToDisplay(card);
if (!defaultPrint?.backImage) {
return card.back?.image_uris;
}
return { en: defaultPrint.backImage };
};

const defaultPrintToDisplay = (card: Card) => {
if (!replaceAltArts.value || card.is_custom) {
return undefined;
}
const defaultPrint = getDefaultPrint(card);
if (!defaultPrint || defaultPrint.id === card.id) {
return undefined;
}
return defaultPrint;
};
14 changes: 8 additions & 6 deletions client/src/components/CardImage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,9 @@ import ClazyLoad from "./../vue-clazy-load.vue";
import { computed } from "vue";
import { Language } from "@/Types";
import { Card, CardFace } from "@/CardTypes";
import { useCardCache, ScryfallCard, isReady, ScryfallCardFace, CardCacheEntry } from "../vueCardCache";
import type { ScryfallCard, ScryfallCardFace } from "../Scryfall";
import { useCardCache, isReady, CardCacheEntry } from "../vueCardCache";
import { displayedBackImageURIs, displayedFrontImageURIs } from "../altArtDisplay";

const { cardCache } = useCardCache();

Expand All @@ -173,17 +175,17 @@ const props = withDefaults(
}
);
const imageURI = computed(() => {
if (props.language in props.card.image_uris) return props.card.image_uris[props.language];
return props.card.image_uris["en"];
const imageURIs = displayedFrontImageURIs(props.card);
if (props.language in imageURIs) return imageURIs[props.language];
return imageURIs["en"];
});
const hasBack = computed(() => {
return props.card.back !== null && props.card.back !== undefined;
});
const backImageURI = computed(() => {
if (!hasBack.value) return undefined;
return props.language in props.card.back!.image_uris
? props.card.back?.image_uris[props.language]
: props.card.back?.image_uris["en"];
const imageURIs = displayedBackImageURIs(props.card)!;
return props.language in imageURIs ? imageURIs[props.language] : imageURIs["en"];
});
const cardAdditionalData = computed(() => {
if (!props.displayCardText) return false; // Don't send the requests automatically
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/CardPlaceholder.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
</template>

<script setup lang="ts">
import { ScryfallCard, ScryfallCardFace } from "../vueCardCache";
import type { ScryfallCard, ScryfallCardFace } from "../Scryfall";
import CardText from "./CardText.vue";
import { Card, CardFace } from "@/CardTypes";

Expand Down
3 changes: 2 additions & 1 deletion client/src/components/CardPopup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ import CardText from "./CardText.vue";
import CardImage from "./CardImage.vue";
import { isString } from "../../../src/TypeChecks";
import { Card, CardFace, CardID } from "@/CardTypes";
import { useCardCache, ScryfallCard, isReady, CardCacheEntry } from "../vueCardCache";
import type { ScryfallCard } from "../Scryfall";
import { useCardCache, isReady, CardCacheEntry } from "../vueCardCache";
const { cardCache } = useCardCache();

import { useEmitter } from "../appCommon";
Expand Down
3 changes: 2 additions & 1 deletion client/src/components/CardText.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
</template>

<script setup lang="ts">
import { ScryfallCard, isScryfallCard, ScryfallCardFace, CardCacheEntry, isScryfallCardFace } from "../vueCardCache";
import { ScryfallCard, isScryfallCard, ScryfallCardFace, isScryfallCardFace } from "../Scryfall";
import { CardCacheEntry } from "../vueCardCache";
import { ref, computed, watch, onMounted, nextTick, onUnmounted } from "vue";
import { replaceManaSymbols, parseManaSymbols } from "../ManaSymbols";
import { Card, CardFace } from "@/CardTypes";
Expand Down
9 changes: 6 additions & 3 deletions client/src/components/DraftLogPicksSummary.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@
:key="`${cardIdx}_${pickIdx}`"
v-tooltip.left="{
html: true,
content: `<img src='${
card.image_uris[language] ?? card.image_uris['en']
}' style='max-height: 40vh; max-width: 40vw'/>`,
content: `<img src='${tooltipImageURI(card)}' style='max-height: 40vh; max-width: 40vw'/>`,
}"
@contextmenu="toggleZoom($event, card)"
@mouseleave="mouseLeave"
Expand All @@ -34,6 +32,7 @@
<script setup lang="ts">
import { Card, CardID } from "@/CardTypes";
import { replaceManaSymbols } from "../ManaSymbols";
import { displayedFrontImageURIs } from "../altArtDisplay";
import { useEmitter } from "../appCommon";
const { emitter } = useEmitter();

Expand All @@ -52,6 +51,10 @@ function getPicks(packIdx: number, pickIdx: number) {
const pick = props.picks[packIdx][pickIdx];
return pick.pick.map((card_idx) => props.carddata[pick.booster[card_idx]]);
}
const tooltipImageURI = (card: Card) => {
const imageURIs = displayedFrontImageURIs(card);
return imageURIs[props.language] ?? imageURIs.en;
};
function toggleZoom(e: Event, card: Card) {
e.preventDefault();
emitter.emit("togglecardpopup", e, card);
Expand Down
Loading
Loading