import * as React from "react"; import gql from "graphql-tag"; import { useQuery, useMutation } from "@apollo/client"; import { css } from "emotion"; import { Badge, Box, Drawer, DrawerBody, DrawerCloseButton, DrawerContent, DrawerHeader, DrawerOverlay, FormControl, FormErrorMessage, FormHelperText, FormLabel, HStack, Link, Select, Spinner, Stack, useBreakpointValue, useColorModeValue, useDisclosure, } from "@chakra-ui/core"; import { CheckCircleIcon, EditIcon, ExternalLinkIcon } from "@chakra-ui/icons"; import ItemLayerSupportModal from "./ItemLayerSupportModal"; import { OutfitLayers } from "../../components/OutfitPreview"; import useOutfitAppearance from "../../components/useOutfitAppearance"; import { OutfitStateContext } from "../useOutfitState"; import useSupportSecret from "./useSupportSecret"; /** * ItemSupportDrawer shows Support UI for the item when open. * * This component controls the drawer element. The actual content is imported * from another lazy-loaded component! */ function ItemSupportDrawer({ item, isOpen, onClose }) { const placement = useBreakpointValue({ base: "bottom", lg: "right", // TODO: There's a bug in the Chakra RC that doesn't read the breakpoint // specification correctly - we need these extra keys until it's fixed! // https://github.com/chakra-ui/chakra-ui/issues/1444 0: "bottom", 1: "bottom", 2: "right", 3: "right", }); return ( {item.name} Support ); } function ItemSupportSpecialColorFields({ item }) { const supportSecret = useSupportSecret(); const { loading: itemLoading, error: itemError, data: itemData } = useQuery( gql` query ItemSupportDrawerManualSpecialColor($itemId: ID!) { item(id: $itemId) { id manualSpecialColor { id } } } `, { variables: { itemId: item.id }, // HACK: I think it's a bug in @apollo/client 3.1.1 that, if the // optimistic response sets `manualSpecialColor` to null, the query // doesn't update, even though its cache has updated :/ // // This cheap trick of changing the display name every re-render // persuades Apollo that this is a different query, so it re-checks // its cache and finds the empty `manualSpecialColor`. Weird! displayName: `ItemSupportDrawerManualSpecialColor-${new Date()}`, } ); const { loading: colorsLoading, error: colorsError, data: colorsData, } = useQuery( gql` query ItemSupportDrawerAllColors { allColors { id name isStandard } } ` ); const [ mutate, { loading: mutationLoading, error: mutationError, data: mutationData }, ] = useMutation(gql` mutation ItemSupportDrawerSetManualSpecialColor( $itemId: ID! $colorId: ID $supportSecret: String! ) { setManualSpecialColor( itemId: $itemId colorId: $colorId supportSecret: $supportSecret ) { id manualSpecialColor { id } } } `); const nonStandardColors = colorsData?.allColors?.filter((c) => !c.isStandard) || []; nonStandardColors.sort((a, b) => a.name.localeCompare(b.name)); const linkColor = useColorModeValue("green.500", "green.300"); return ( Special color {colorsError && ( {colorsError.message} )} {itemError && {itemError.message}} {mutationError && ( {mutationError.message} )} {!colorsError && !itemError && !mutationError && ( This controls which previews we show on the{" "} item page . )} ); } function ItemSupportPetCompatibilityRuleFields({ item }) { return ( Pet compatibility rule By default, we assume Background-y zones fit all pets the same. When items don't follow that rule, we can override it. ); } /** * NOTE: This component takes `outfitState` from context, rather than as a prop * from its parent, for performance reasons. We want `Item` to memoize * and generally skip re-rendering on `outfitState` changes, and to make * sure the context isn't accessed when the drawer is closed. So we use * it here, only when the drawer is open! */ function ItemSupportAppearanceFields({ item }) { const outfitState = React.useContext(OutfitStateContext); const { speciesId, colorId, pose } = outfitState; const { error, visibleLayers } = useOutfitAppearance({ speciesId, colorId, pose, wornItemIds: [item.id], }); const biologyLayers = visibleLayers.filter((l) => l.source === "pet"); const itemLayers = visibleLayers.filter((l) => l.source === "item"); itemLayers.sort((a, b) => a.zone.depth - b.zone.depth); return ( Appearance layers {itemLayers.map((itemLayer) => ( ))} {error && {error.message}} ); } function ItemSupportAppearanceLayer({ item, itemLayer, biologyLayers, outfitState, }) { const { isOpen, onOpen, onClose } = useDisclosure(); const iconButtonBgColor = useColorModeValue("green.100", "green.300"); const iconButtonColor = useColorModeValue("green.800", "gray.900"); return ( {itemLayer.zone.label} Zone ID: {itemLayer.zone.id} DTI ID: {itemLayer.id} ); } export default ItemSupportDrawer;