diff --git a/dev-todos.txt b/dev-todos.txt index 5e18a98..33f3e64 100644 --- a/dev-todos.txt +++ b/dev-todos.txt @@ -1,3 +1,4 @@ * Use accessible click targets for item lists! Honestly, can they be checkboxes? * Pagination for search queries, right now we LIMIT 30 * Search needs to restrict by fit! +* Undo the local linking we did for @chakra-ui/core, react, and react-dom on Matchu's machine 😅 diff --git a/src/OutfitPreview.js b/src/OutfitPreview.js index bfef3de..b3c637a 100644 --- a/src/OutfitPreview.js +++ b/src/OutfitPreview.js @@ -2,7 +2,17 @@ import React from "react"; import { CSSTransition, TransitionGroup } from "react-transition-group"; import gql from "graphql-tag"; import { useQuery } from "@apollo/react-hooks"; -import { Flex, Image, Spinner, Text, Icon, Box } from "@chakra-ui/core"; +import { + Box, + Flex, + Icon, + IconButton, + Image, + PseudoBox, + Spinner, + Text, + Tooltip, +} from "@chakra-ui/core"; import { Delay } from "./util"; @@ -49,6 +59,11 @@ function OutfitPreview({ outfitState }) { } ); + const visibleLayers = getVisibleLayers(data); + const [downloadImageUrl, prepareDownload] = useDownloadableImage( + visibleLayers + ); + if (error) { return ( @@ -62,9 +77,9 @@ function OutfitPreview({ outfitState }) { } return ( - + - {getVisibleLayers(data).map((layer) => ( + {visibleLayers.map((layer) => ( @@ -101,7 +118,39 @@ function OutfitPreview({ outfitState }) { )} - + + + + ); } @@ -152,4 +201,49 @@ function FullScreenCenter({ children }) { ); } +function useDownloadableImage(visibleLayers) { + const [downloadImageUrl, setDownloadImageUrl] = React.useState(null); + const [preparedForLayerIds, setPreparedForLayerIds] = React.useState([]); + + const prepareDownload = React.useCallback(async () => { + // Skip if the current image URL is already correct for these layers. + const layerIds = visibleLayers.map((l) => l.id); + if (layerIds.join(",") === preparedForLayerIds.join(",")) { + return; + } + + const imagePromises = visibleLayers.map( + (layer) => + new Promise((resolve, reject) => { + const image = new window.Image(); + image.crossOrigin = "Anonymous"; // Requires S3 CORS config! + image.addEventListener("load", () => resolve(image), false); + image.addEventListener("error", (e) => reject(e), false); + image.src = layer.imageUrl; + }) + ); + + const images = await Promise.all(imagePromises); + + const canvas = document.createElement("canvas"); + const context = canvas.getContext("2d"); + canvas.width = 600; + canvas.height = 600; + + for (const image of images) { + context.drawImage(image, 0, 0); + } + + console.log( + "Generated image for download", + layerIds, + canvas.toDataURL("image/png") + ); + setDownloadImageUrl(canvas.toDataURL("image/png")); + setPreparedForLayerIds(layerIds); + }, [preparedForLayerIds, visibleLayers]); + + return [downloadImageUrl, prepareDownload]; +} + export default OutfitPreview; diff --git a/yarn.lock b/yarn.lock index 2e57e44..ecc790b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5403,9 +5403,9 @@ flush-write-stream@^1.0.0: readable-stream "^2.3.6" focus-lock@^0.6.7: - version "0.6.7" - resolved "https://registry.yarnpkg.com/focus-lock/-/focus-lock-0.6.7.tgz#65e298f2ba2a3372ab57a4e4c4bdc19e1e32a4e5" - integrity sha512-KRo93U/afEqt7w5tBm4t0FHf/Li8tEYav3n4GUiZdeRlRfrtMbL8yQg0xRVnY/kmBRmQ4xkqIlbaMvuqlu53kg== + version "0.6.8" + resolved "https://registry.yarnpkg.com/focus-lock/-/focus-lock-0.6.8.tgz#61985fadfa92f02f2ee1d90bc738efaf7f3c9f46" + integrity sha512-vkHTluRCoq9FcsrldC0ulQHiyBYgVJB2CX53I8r0nTC6KnEij7Of0jpBspjt3/CuNb6fyoj3aOh9J2HgQUM0og== follow-redirects@^1.0.0: version "1.10.0"