import React from "react"; import gql from "graphql-tag"; import { useMutation, useQuery } from "@apollo/client"; import { Box, Button, IconButton, Select, Spinner, Switch, Wrap, WrapItem, useDisclosure, UnorderedList, ListItem, } from "@chakra-ui/react"; import { ArrowBackIcon, ArrowForwardIcon, CheckCircleIcon, EditIcon, } from "@chakra-ui/icons"; import HangerSpinner from "../../components/HangerSpinner"; import Metadata, { MetadataLabel, MetadataValue } from "./Metadata"; import useSupport from "./useSupport"; import AppearanceLayerSupportModal from "./AppearanceLayerSupportModal"; import { petAppearanceForPosePickerFragment } from "../PosePicker"; function PosePickerSupport({ speciesId, colorId, pose, appearanceId, initialFocusRef, dispatchToOutfit, }) { const { loading, error, data } = useQuery( gql` query PosePickerSupport($speciesId: ID!, $colorId: ID!) { petAppearances(speciesId: $speciesId, colorId: $colorId) { id pose isGlitched layers { id zone { id label @client } # For AppearanceLayerSupportModal remoteId bodyId swfUrl svgUrl imageUrl: imageUrlV2(idealSize: SIZE_600) canvasMovieLibraryUrl } restrictedZones { id label @client } # For AppearanceLayerSupportModal to know the name species { id name } color { id name } # Also, anything the PosePicker wants that isn't here, so that we # don't have to refetch anything when we change the canonical poses. ...PetAppearanceForPosePicker } ...CanonicalPetAppearances } ${canonicalPetAppearancesFragment} ${petAppearanceForPosePickerFragment} `, { variables: { speciesId, colorId } } ); // Resize the Popover when we toggle loading state, because it probably will // affect the content size. appearanceId might also affect content size, if // it occupies different zones. // // NOTE: This also triggers an additional necessary resize when the component // first mounts, because PosePicker lazy-loads it, so it actually // mounting affects size too. React.useLayoutEffect(() => { // HACK: To trigger a Popover resize, we simulate a window resize event, // because Popover listens for window resizes to reposition itself. // I've also filed an issue requesting an official API! // https://github.com/chakra-ui/chakra-ui/issues/1853 window.dispatchEvent(new Event("resize")); }, [loading, appearanceId]); const canonicalAppearanceIdsByPose = { HAPPY_MASC: data?.happyMasc?.id, SAD_MASC: data?.sadMasc?.id, SICK_MASC: data?.sickMasc?.id, HAPPY_FEM: data?.happyFem?.id, SAD_FEM: data?.sadFem?.id, SICK_FEM: data?.sickFem?.id, UNCONVERTED: data?.unconverted?.id, UNKNOWN: data?.unknown?.id, }; const canonicalAppearanceIds = Object.values( canonicalAppearanceIdsByPose ).filter((id) => id); const providedAppearanceId = appearanceId; if (!providedAppearanceId) { appearanceId = canonicalAppearanceIdsByPose[pose]; } // If you don't already have `appearanceId` in the outfit state, opening up // PosePickerSupport adds it! That way, if you make changes that affect the // canonical poses, we'll still stay navigated to this one. React.useEffect(() => { if (!providedAppearanceId && appearanceId) { dispatchToOutfit({ type: "setPose", pose, appearanceId, }); } }, [providedAppearanceId, appearanceId, pose, dispatchToOutfit]); if (loading) { return ( ); } if (error) { return ( {error.message} ); } const currentPetAppearance = data.petAppearances.find( (pa) => pa.id === appearanceId ); if (!currentPetAppearance) { return ( Pet appearance with ID {JSON.stringify(appearanceId)} not found ); } return ( DTI ID: {appearanceId} Pose: Layers: {currentPetAppearance.layers .map((layer) => [`${layer.zone.label} (${layer.zone.id})`, layer]) .sort((a, b) => a[0].localeCompare(b[0])) .map(([text, layer]) => ( {text} ))} Restricts: {currentPetAppearance.restrictedZones.length > 0 ? ( {currentPetAppearance.restrictedZones .map((zone) => `${zone.label} (${zone.id})`) .sort((a, b) => a[0].localeCompare(b[0])) .map((zoneText) => ( {zoneText} ))} ) : ( None )} ); } function PetLayerSupportLink({ outfitState, petAppearance, layer, children }) { const { isOpen, onOpen, onClose } = useDisclosure(); return ( <> ); } function PosePickerSupportNavigator({ petAppearances, currentPetAppearance, canonicalAppearanceIds, dropdownRef, dispatchToOutfit, }) { const currentIndex = petAppearances.indexOf(currentPetAppearance); const prevPetAppearance = petAppearances[currentIndex - 1]; const nextPetAppearance = petAppearances[currentIndex + 1]; return ( } size="sm" marginRight="2" isDisabled={prevPetAppearance == null} onClick={() => dispatchToOutfit({ type: "setPose", pose: prevPetAppearance.pose, appearanceId: prevPetAppearance.id, }) } /> } size="sm" marginLeft="2" isDisabled={nextPetAppearance == null} onClick={() => dispatchToOutfit({ type: "setPose", pose: nextPetAppearance.pose, appearanceId: nextPetAppearance.id, }) } /> ); } function PosePickerSupportPoseFields({ petAppearance, speciesId, colorId }) { const { supportSecret } = useSupport(); const [mutatePose, poseMutation] = useMutation( gql` mutation PosePickerSupportSetPetAppearancePose( $appearanceId: ID! $pose: Pose! $supportSecret: String! ) { setPetAppearancePose( appearanceId: $appearanceId pose: $pose supportSecret: $supportSecret ) { id pose } } `, { refetchQueries: [ { query: gql` query PosePickerSupportRefetchCanonicalAppearances( $speciesId: ID! $colorId: ID! ) { ...CanonicalPetAppearances } ${canonicalPetAppearancesFragment} `, variables: { speciesId, colorId }, }, ], } ); const [mutateIsGlitched, isGlitchedMutation] = useMutation( gql` mutation PosePickerSupportSetPetAppearanceIsGlitched( $appearanceId: ID! $isGlitched: Boolean! $supportSecret: String! ) { setPetAppearanceIsGlitched( appearanceId: $appearanceId isGlitched: $isGlitched supportSecret: $supportSecret ) { id isGlitched } } `, { refetchQueries: [ { query: gql` query PosePickerSupportRefetchCanonicalAppearances( $speciesId: ID! $colorId: ID! ) { ...CanonicalPetAppearances } ${canonicalPetAppearancesFragment} `, variables: { speciesId, colorId }, }, ], } ); return ( {poseMutation.error && ( {poseMutation.error.message} )} {isGlitchedMutation.error && ( {isGlitchedMutation.error.message} )} ); } export function PosePickerSupportSwitch({ isChecked, onChange }) { return ( 💖 ); } const POSE_NAMES = { HAPPY_MASC: "Happy Masc", HAPPY_FEM: "Happy Fem", SAD_MASC: "Sad Masc", SAD_FEM: "Sad Fem", SICK_MASC: "Sick Masc", SICK_FEM: "Sick Fem", UNCONVERTED: "Unconverted", UNKNOWN: "Unknown", }; const canonicalPetAppearancesFragment = gql` fragment CanonicalPetAppearances on Query { happyMasc: petAppearance( speciesId: $speciesId colorId: $colorId pose: HAPPY_MASC ) { id } sadMasc: petAppearance( speciesId: $speciesId colorId: $colorId pose: SAD_MASC ) { id } sickMasc: petAppearance( speciesId: $speciesId colorId: $colorId pose: SICK_MASC ) { id } happyFem: petAppearance( speciesId: $speciesId colorId: $colorId pose: HAPPY_FEM ) { id } sadFem: petAppearance( speciesId: $speciesId colorId: $colorId pose: SAD_FEM ) { id } sickFem: petAppearance( speciesId: $speciesId colorId: $colorId pose: SICK_FEM ) { id } unconverted: petAppearance( speciesId: $speciesId colorId: $colorId pose: UNCONVERTED ) { id } unknown: petAppearance( speciesId: $speciesId colorId: $colorId pose: UNKNOWN ) { id } } `; export default PosePickerSupport;