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.data ? (
) : undefined
}
onChange={(e) => {
const pose = e.target.value;
mutatePose({
variables: {
appearanceId: petAppearance.id,
pose,
supportSecret,
},
optimisticResponse: {
__typename: "Mutation",
setPetAppearancePose: {
__typename: "PetAppearance",
id: petAppearance.id,
pose,
},
},
}).catch((e) => {
/* Discard errors here; we'll show them in the UI! */
});
}}
isInvalid={poseMutation.error != null}
>
{Object.entries(POSE_NAMES).map(([pose, name]) => (
))}
) : isGlitchedMutation.data ? (
) : undefined
}
onChange={(e) => {
const isGlitched = e.target.value === "true";
mutateIsGlitched({
variables: {
appearanceId: petAppearance.id,
isGlitched,
supportSecret,
},
optimisticResponse: {
__typename: "Mutation",
setPetAppearanceIsGlitched: {
__typename: "PetAppearance",
id: petAppearance.id,
isGlitched,
},
},
}).catch((e) => {
/* Discard errors here; we'll show them in the UI! */
});
}}
isInvalid={isGlitchedMutation.error != null}
>
{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;