diff --git a/src/OutfitPreview.js b/src/OutfitPreview.js index e5c06d6..7d6a5c9 100644 --- a/src/OutfitPreview.js +++ b/src/OutfitPreview.js @@ -15,6 +15,7 @@ import { } from "@chakra-ui/core"; import { Delay } from "./util"; +import OutfitResetModal from "./OutfitResetModal"; import SpeciesColorPicker from "./SpeciesColorPicker"; import "./OutfitPreview.css"; @@ -39,6 +40,7 @@ export const itemAppearanceFragment = gql` function OutfitPreview({ outfitState, dispatchToOutfit }) { const { wornItemIds, speciesId, colorId } = outfitState; const [hasFocus, setHasFocus] = React.useState(false); + const [showResetModal, setShowResetModal] = React.useState(false); const { loading, error, data } = useQuery( gql` @@ -123,6 +125,25 @@ function OutfitPreview({ outfitState, dispatchToOutfit }) { )} + + setHasFocus(true)} + onBlur={() => setHasFocus(false)} + onClick={() => setShowResetModal(true)} + /> + + setShowResetModal(false)} + dispatchToOutfit={dispatchToOutfit} + /> ); } diff --git a/src/OutfitResetModal.js b/src/OutfitResetModal.js new file mode 100644 index 0000000..ba90cdf --- /dev/null +++ b/src/OutfitResetModal.js @@ -0,0 +1,125 @@ +import React from "react"; +import { + Text, + Modal, + ModalOverlay, + ModalContent, + ModalHeader, + ModalCloseButton, + ModalBody, + Input, + Button, + ModalFooter, + FormErrorMessage, + FormControl, +} from "@chakra-ui/core"; + +function OutfitResetModal({ isOpen, onClose, dispatchToOutfit }) { + const [petName, setPetName] = React.useState(""); + + const onComplete = ({ custom_pet, object_info_registry }) => { + dispatchToOutfit({ + type: "reset", + name: custom_pet.name, + speciesId: custom_pet.species_id, + colorId: custom_pet.color_id, + wornItemIds: Object.values(object_info_registry).map( + (o) => o.obj_info_id + ), + closetedItemIds: [], + }); + onClose(); + setPetName(""); + }; + const { loading, error, loadOutfitData } = useLoadOutfitData( + petName, + onComplete + ); + + return ( + + + +
{ + e.preventDefault(); + loadOutfitData(); + }} + > + + + Want to try your own pet?{" "} + + 😮 + + + + + + + Choose a pet from Neopets.com, and we'll pull their outfit data + into here for you to play with! + + + setPetName(e.target.value)} + autoFocus + /> + {error && ( + + We had trouble loading that pet, sorry{" "} + + 😖 + + + )} + + + + + + +
+
+ ); +} + +function useLoadOutfitData(petName, onComplete) { + const [loading, setLoading] = React.useState(false); + const [error, setError] = React.useState(null); + + const loadOutfitData = async () => { + setLoading(true); + setError(null); + + let json; + try { + const res = await fetch( + `http://www.neopets.com/amfphp/json.php/CustomPetService.getViewerData` + + `/${petName}` + ); + if (!res.ok) { + throw new Error(res.statusText); + } + json = await res.json(); + if (!json.custom_pet) { + throw new Error(`missing custom_pet data`); + } + } catch (e) { + setLoading(false); + setError(e); + return; + } + + setLoading(false); + onComplete(json); + }; + + return { loading, error, loadOutfitData }; +} + +export default OutfitResetModal; diff --git a/src/useOutfitState.js b/src/useOutfitState.js index 9938e7d..164e81a 100644 --- a/src/useOutfitState.js +++ b/src/useOutfitState.js @@ -146,6 +146,15 @@ const outfitStateReducer = (apolloClient) => (baseState, action) => { wornItemIds.delete(itemId); closetedItemIds.delete(itemId); }); + case "reset": + const { name, speciesId, colorId, wornItemIds, closetedItemIds } = action; + return { + name, + speciesId: String(speciesId), + colorId: String(colorId), + wornItemIds: new Set(wornItemIds.map(String)), + closetedItemIds: new Set(closetedItemIds.map(String)), + }; default: throw new Error(`unexpected action ${JSON.stringify(action)}`); }