diff --git a/src/app/WardrobePage/OutfitControls.js b/src/app/WardrobePage/OutfitControls.js index 814d77b..8265192 100644 --- a/src/app/WardrobePage/OutfitControls.js +++ b/src/app/WardrobePage/OutfitControls.js @@ -43,6 +43,7 @@ import { loadImage, useLocalStorage } from "../util"; import useCurrentUser from "../components/useCurrentUser"; import useOutfitAppearance from "../components/useOutfitAppearance"; import OutfitKnownGlitchesBadge from "./OutfitKnownGlitchesBadge"; +import usePreferArchive from "../components/usePreferArchive"; /** * OutfitControls is the set of controls layered over the outfit preview, to @@ -500,28 +501,56 @@ function SettingsButton({ onLockFocus, onUnlockFocus }) { function HiResModeSetting() { const [hiResMode, setHiResMode] = useLocalStorage("DTIHiResMode", false); + const [preferArchive, setPreferArchive] = usePreferArchive(); return ( - - - - - Hi-res mode (SVG) - - - Crisper at higher resolutions, but not always accurate - - - - setHiResMode(e.target.checked)} - /> - - + + + + + + Hi-res mode (SVG) + + + Crisper at higher resolutions, but not always accurate + + + + setHiResMode(e.target.checked)} + /> + + + + + + + + Use DTI's image archive + + + Turn this on when images.neopets.com is slow! + + + + setPreferArchive(e.target.checked)} + /> + + + ); } @@ -585,6 +614,7 @@ function ControlButton({ icon, "aria-label": ariaLabel, ...props }) { */ function useDownloadableImage(visibleLayers) { const [hiResMode] = useLocalStorage("DTIHiResMode", false); + const [preferArchive] = usePreferArchive(); const [downloadImageUrl, setDownloadImageUrl] = React.useState(null); const [preparedForLayerIds, setPreparedForLayerIds] = React.useState([]); @@ -611,6 +641,7 @@ function useDownloadableImage(visibleLayers) { const imagePromises = visibleLayers.map((layer) => loadImage(getBestImageUrlForLayer(layer, { hiResMode }), { crossOrigin: "anonymous", + preferArchive, }) ); @@ -644,7 +675,7 @@ function useDownloadableImage(visibleLayers) { ); setDownloadImageUrl(canvas.toDataURL("image/png")); setPreparedForLayerIds(layerIds); - }, [preparedForLayerIds, visibleLayers, toast, hiResMode]); + }, [preparedForLayerIds, visibleLayers, toast, hiResMode, preferArchive]); return [downloadImageUrl, prepareDownload]; } diff --git a/src/app/components/ItemCard.js b/src/app/components/ItemCard.js index faa0461..b87237e 100644 --- a/src/app/components/ItemCard.js +++ b/src/app/components/ItemCard.js @@ -21,6 +21,7 @@ import Link from "next/link"; import SquareItemCard from "./SquareItemCard"; import { safeImageUrl, useCommonStyles } from "../util"; +import usePreferArchive from "./usePreferArchive"; function ItemCard({ item, badges, variant = "list", ...props }) { const { brightBackground } = useCommonStyles(); @@ -105,6 +106,7 @@ export function ItemThumbnail({ focusSelector, ...props }) { + const [preferArchive] = usePreferArchive(); const theme = useTheme(); const borderColor = useColorModeValue( @@ -170,7 +172,7 @@ export function ItemThumbnail({ as="img" width="100%" height="100%" - src={safeImageUrl(item.thumbnailUrl)} + src={safeImageUrl(item.thumbnailUrl, { preferArchive })} alt={`Thumbnail art for ${item.name}`} /> )} diff --git a/src/app/components/OutfitMovieLayer.js b/src/app/components/OutfitMovieLayer.js index ac8c06e..81248cb 100644 --- a/src/app/components/OutfitMovieLayer.js +++ b/src/app/components/OutfitMovieLayer.js @@ -3,6 +3,7 @@ import LRU from "lru-cache"; import { Box, Grid, useToast } from "@chakra-ui/react"; import { loadImage, logAndCapture, safeImageUrl } from "../util"; +import usePreferArchive from "./usePreferArchive"; // Import EaselJS and TweenJS directly into the `window` object! The bundled // scripts are built to attach themselves to `window.createjs`, and @@ -24,6 +25,7 @@ function OutfitMovieLayer({ onLowFps = null, canvasProps = {}, }) { + const [preferArchive] = usePreferArchive(); const [stage, setStage] = React.useState(null); const [library, setLibrary] = React.useState(null); const [movieClip, setMovieClip] = React.useState(null); @@ -129,7 +131,7 @@ function OutfitMovieLayer({ React.useEffect(() => { let canceled = false; - const movieLibraryPromise = loadMovieLibrary(libraryUrl); + const movieLibraryPromise = loadMovieLibrary(libraryUrl, { preferArchive }); movieLibraryPromise .then((library) => { if (canceled) { @@ -154,7 +156,7 @@ function OutfitMovieLayer({ setLibrary(null); setMovieClip(null); }; - }, [libraryUrl, onError]); + }, [libraryUrl, preferArchive, onError]); // This effect puts the `movieClip` on the `stage`, when both are ready. React.useEffect(() => { @@ -305,7 +307,7 @@ function loadScriptTag(src) { const MOVIE_LIBRARY_CACHE = new LRU(10); -export function loadMovieLibrary(librarySrc) { +export function loadMovieLibrary(librarySrc, { preferArchive = false } = {}) { const cancelableResourcePromises = []; const cancelAllResources = () => cancelableResourcePromises.forEach((p) => p.cancel()); @@ -323,7 +325,9 @@ export function loadMovieLibrary(librarySrc) { } // Then, load the script tag. (Make sure we set it up to be cancelable!) - const scriptPromise = loadScriptTag(safeImageUrl(librarySrc)); + const scriptPromise = loadScriptTag( + safeImageUrl(librarySrc, { preferArchive }) + ); cancelableResourcePromises.push(scriptPromise); await scriptPromise; @@ -372,6 +376,7 @@ export function loadMovieLibrary(librarySrc) { id, loadImage(librarySrcDir + "/" + src, { crossOrigin: "anonymous", + preferArchive, }), ]) ); diff --git a/src/app/components/OutfitPreview.js b/src/app/components/OutfitPreview.js index 940c398..7e3e263 100644 --- a/src/app/components/OutfitPreview.js +++ b/src/app/components/OutfitPreview.js @@ -20,6 +20,7 @@ import OutfitMovieLayer, { import HangerSpinner from "./HangerSpinner"; import { loadImage, safeImageUrl, useLocalStorage } from "../util"; import useOutfitAppearance from "./useOutfitAppearance"; +import usePreferArchive from "./usePreferArchive"; /** * OutfitPreview is for rendering a full outfit! It accepts outfit data, @@ -169,6 +170,7 @@ export function OutfitLayers({ ...props }) { const [hiResMode] = useLocalStorage("DTIHiResMode", false); + const [preferArchive] = usePreferArchive(); const containerRef = React.useRef(null); const [canvasSize, setCanvasSize] = React.useState(0); @@ -281,7 +283,8 @@ export function OutfitLayers({ ({ library, @@ -465,7 +471,7 @@ export function usePreloadLayers(layers) { return () => { canceled = true; }; - }, [layers, hiResMode]); + }, [layers, hiResMode, preferArchive]); return { loading, error, loadedLayers, layersHaveAnimations }; } diff --git a/src/app/components/SquareItemCard.js b/src/app/components/SquareItemCard.js index 09df265..1ed643a 100644 --- a/src/app/components/SquareItemCard.js +++ b/src/app/components/SquareItemCard.js @@ -12,6 +12,7 @@ import Link from "next/link"; import { safeImageUrl, useCommonStyles } from "../util"; import { CheckIcon, CloseIcon, StarIcon } from "@chakra-ui/icons"; +import usePreferArchive from "./usePreferArchive"; function SquareItemCard({ item, @@ -183,6 +184,7 @@ function SquareItemCardLayout({ } function ItemThumbnail({ item, tradeMatchingMode }) { + const [preferArchive] = usePreferArchive(); const kindColorScheme = item.isNc ? "purple" : item.isPb ? "orange" : "gray"; const thumbnailShadowColor = useColorModeValue( @@ -229,7 +231,7 @@ function ItemThumbnail({ item, tradeMatchingMode }) { > {/* eslint-disable-next-line @next/next/no-img-element */}