impress-2020/src/app/InternalAssetImagePage.js
Matchu f036890aa1 Use /api/assetImage for all image sizes
We update /api/assetImage to accept size as a parameter (I make it mandatory to push people into HTTP caching happy paths), and we update the GraphQL thing to use it in those cases too!

This also means that, if these images seem to go well, we could swap Classic DTI over to them… I want to turn off those RAM-heavy image converters on the VPS lol
2021-08-19 17:56:09 -07:00

132 lines
3.1 KiB
JavaScript

import React from "react";
import { Box, Center } from "@chakra-ui/react";
import { useLocation } from "react-router-dom";
import * as Sentry from "@sentry/react";
import { Global, css } from "@emotion/react";
import OutfitMovieLayer from "./components/OutfitMovieLayer";
/**
* We use this in /api/assetImage, to render the asset image! The headless
* browser navigates here, and screenshots the canvas once it loads.
*/
function InternalAssetImagePage() {
return (
<Box padding="4">
<Sentry.ErrorBoundary
fallback={({ error }) => (
<AssetImageErrorMessage>
Unexpected error: {error.message}
</AssetImageErrorMessage>
)}
>
<InternalAssetImagePageContent />
</Sentry.ErrorBoundary>
<Global
// We remove the default body background, so that the headless browser
// can take the screenshot with transparency.
styles={css`
body {
background: transparent;
}
`}
/>
</Box>
);
}
function InternalAssetImagePageContent() {
const location = useLocation();
const search = new URLSearchParams(location.search);
const libraryUrl = search.get("libraryUrl");
const size = search.get("size") || "600";
const [movieError, setMovieError] = React.useState(null);
const onMovieError = React.useCallback((error) => {
console.error("Error playing movie:", error);
setMovieError(error);
}, []);
if (!libraryUrl) {
return (
<AssetImageErrorMessage>
Error: libraryUrl parameter is required
</AssetImageErrorMessage>
);
}
if (!isNeopetsUrl(libraryUrl)) {
return (
<AssetImageErrorMessage>
Error: libraryUrl must be an HTTPS Neopets URL, but was:{" "}
<code>{JSON.stringify(libraryUrl)}</code>
</AssetImageErrorMessage>
);
}
if (size !== "600" && size !== "300" && size !== "150") {
return (
<AssetImageErrorMessage>
Error: size must be 600, 300, or 150, but was: {size}
</AssetImageErrorMessage>
);
}
if (movieError) {
return (
<AssetImageErrorMessage>
Error playing movie: {movieError.message}
</AssetImageErrorMessage>
);
}
return (
<Box
border="1px solid"
borderColor="green.400"
boxSizing="content-box"
width={parseInt(size)}
height={parseInt(size)}
>
<OutfitMovieLayer
libraryUrl={libraryUrl}
width={parseInt(size)}
height={parseInt(size)}
onError={onMovieError}
canvasProps={{ id: "asset-image-canvas" }}
isPaused
/>
</Box>
);
}
function isNeopetsUrl(urlString) {
let url;
try {
url = new URL(urlString);
} catch (e) {
return false;
}
return url.origin === "https://images.neopets.com";
}
function AssetImageErrorMessage({ children }) {
return (
<Center
width="600px"
height="600px"
color="red.400"
border="1px solid"
borderColor="red.400"
textAlign="center"
padding="4"
id="asset-image-error-message"
>
<Box>{children}</Box>
</Center>
);
}
export default InternalAssetImagePage;