diff --git a/src/app/WardrobePage/support/AppearanceLayerSupportModal.js b/src/app/WardrobePage/support/AppearanceLayerSupportModal.js index 00bcca9..48ca453 100644 --- a/src/app/WardrobePage/support/AppearanceLayerSupportModal.js +++ b/src/app/WardrobePage/support/AppearanceLayerSupportModal.js @@ -41,7 +41,8 @@ import useSupport from "./useSupport"; * appearance layer. Open it by clicking a layer from ItemSupportDrawer. */ function AppearanceLayerSupportModal({ - item, + item, // Specify this or `petAppearance` + petAppearance, // Specify this or `item` layer, outfitState, // speciesId, colorId, pose isOpen, @@ -62,12 +63,16 @@ function AppearanceLayerSupportModal({ const { supportSecret } = useSupport(); const toast = useToast(); + const parentName = item + ? item.name + : `${petAppearance.color.name} ${petAppearance.species.name} ${petAppearance.id}`; + const [ mutate, { loading: mutationLoading, error: mutationError }, ] = useMutation( gql` - mutation ItemSupportSetLayerBodyId( + mutation ApperanceLayerSupportSetLayerBodyId( $layerId: ID! $bodyId: ID! $knownGlitches: [AppearanceLayerKnownGlitch!]! @@ -134,7 +139,7 @@ function AppearanceLayerSupportModal({ onClose(); toast({ status: "success", - title: `Saved layer ${layer.id}: ${item.name}`, + title: `Saved layer ${layer.id}: ${parentName}`, }); }, } @@ -153,7 +158,7 @@ function AppearanceLayerSupportModal({ - Layer {layer.id}: {item.name} + Layer {layer.id}: {parentName} @@ -244,45 +249,55 @@ function AppearanceLayerSupportModal({ SWF - - setUploadModalIsOpen(false)} - /> + {item && ( + <> + + setUploadModalIsOpen(false)} + /> + + )} - - + {item && ( + <> + + + + )} - + {item && ( + + )} {mutationError && ( + mutate().catch((e) => { + /* Discard errors here; we'll show them in the UI! */ + }) + } flex="0 0 auto" > Save changes diff --git a/src/app/WardrobePage/support/PosePickerSupport.js b/src/app/WardrobePage/support/PosePickerSupport.js index 1061e76..853330f 100644 --- a/src/app/WardrobePage/support/PosePickerSupport.js +++ b/src/app/WardrobePage/support/PosePickerSupport.js @@ -1,16 +1,28 @@ import React from "react"; import gql from "graphql-tag"; import { useMutation, useQuery } from "@apollo/client"; -import { Box, IconButton, Select, Spinner, Switch } from "@chakra-ui/react"; +import { + Box, + Button, + IconButton, + Select, + Spinner, + Switch, + Wrap, + WrapItem, + useDisclosure, +} 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"; function PosePickerSupport({ speciesId, @@ -33,6 +45,24 @@ function PosePickerSupport({ id label @client } + + # For AppearanceLayerSupportModal + remoteId + bodyId + swfUrl + svgUrl + imageUrl + canvasMovieLibraryUrl + } + + # For AppearanceLayerSupportModal to know the name + species { + id + name + } + color { + id + name } } @@ -128,19 +158,49 @@ function PosePickerSupport({ colorId={colorId} /> - Zones: + Layers: - {currentPetAppearance.layers - .map((l) => l.zone) - .map((z) => `${z.label} (${z.id})`) - .sort() - .join(", ")} + + {currentPetAppearance.layers + .map((layer) => [`${layer.zone.label} (${layer.zone.id})`, layer]) + .sort((a, b) => a[0].localeCompare(b[0])) + .map(([text, layer]) => ( + + + {text} + + + + ))} + ); } +function PetLayerSupportLink({ outfitState, petAppearance, layer, children }) { + const { isOpen, onOpen, onClose } = useDisclosure(); + return ( + <> + + + + ); +} + function PosePickerSupportNavigator({ petAppearances, currentPetAppearance, diff --git a/src/server/types/MutationsForSupport.js b/src/server/types/MutationsForSupport.js index ae1a008..3889392 100644 --- a/src/server/types/MutationsForSupport.js +++ b/src/server/types/MutationsForSupport.js @@ -18,13 +18,13 @@ let auth0; */ function getAuth0() { if (!auth0) { - auth0 = new ManagementClient({ - domain: "openneo.us.auth0.com", - clientId: process.env.AUTH0_SUPPORT_CLIENT_ID, - clientSecret: process.env.AUTH0_SUPPORT_CLIENT_SECRET, - scope: "read:users update:users", - }); -} + auth0 = new ManagementClient({ + domain: "openneo.us.auth0.com", + clientId: process.env.AUTH0_SUPPORT_CLIENT_ID, + clientSecret: process.env.AUTH0_SUPPORT_CLIENT_SECRET, + scope: "read:users update:users", + }); + } return auth0; } @@ -412,6 +412,10 @@ const resolvers = { itemTranslationLoader, swfAssetLoader, zoneTranslationLoader, + petStateLoader, + petTypeLoader, + colorTranslationLoader, + speciesTranslationLoader, db, } ) => { @@ -453,44 +457,91 @@ const resolvers = { if (process.env["SUPPORT_TOOLS_DISCORD_WEBHOOK_URL"]) { try { - const itemId = await db + const { parentType, parentId } = await db .execute( - `SELECT parent_id FROM parents_swf_assets - WHERE swf_asset_id = ? AND parent_type = "Item" LIMIT 1;`, + `SELECT parent_type, parent_id FROM parents_swf_assets + WHERE swf_asset_id = ? LIMIT 1;`, [layerId] ) - .then(([rows]) => normalizeRow(rows[0]).parentId); + .then(([rows]) => normalizeRow(rows[0])); - const [item, itemTranslation, zoneTranslation] = await Promise.all([ - itemLoader.load(itemId), - itemTranslationLoader.load(itemId), - zoneTranslationLoader.load(oldSwfAsset.zoneId), - ]); + if (parentType === "Item") { + const [item, itemTranslation, zoneTranslation] = await Promise.all([ + itemLoader.load(parentId), + itemTranslationLoader.load(parentId), + zoneTranslationLoader.load(oldSwfAsset.zoneId), + ]); - await logToDiscord({ - embeds: [ - { - title: `🛠 ${itemTranslation.name}`, - thumbnail: { - url: item.thumbnailUrl, - height: 80, - width: 80, - }, - fields: [ - { - name: - `Layer ${layerId} (${zoneTranslation.label}): ` + - `Known glitches`, - value: `${oldSwfAsset.knownGlitches || ""} → **${ - newKnownGlitchesString || "" - }**`, + await logToDiscord({ + embeds: [ + { + title: `🛠 ${itemTranslation.name}`, + thumbnail: { + url: item.thumbnailUrl, + height: 80, + width: 80, }, - ], - timestamp: new Date().toISOString(), - url: `https://impress.openneo.net/items/${itemId}`, - }, - ], - }); + fields: [ + { + name: + `Layer ${layerId} (${zoneTranslation.label}): ` + + `Known glitches`, + value: `${oldSwfAsset.knownGlitches || ""} → **${ + newKnownGlitchesString || "" + }**`, + }, + ], + timestamp: new Date().toISOString(), + url: `https://impress.openneo.net/items/${parentId}`, + }, + ], + }); + } else if (parentType === "PetState") { + const petState = await petStateLoader.load(parentId); + const petType = await petTypeLoader.load(petState.petTypeId); + const [ + colorTranslation, + speciesTranslation, + zoneTranslation, + ] = await Promise.all([ + colorTranslationLoader.load(petType.colorId), + speciesTranslationLoader.load(petType.speciesId), + zoneTranslationLoader.load(oldSwfAsset.zoneId), + ]); + + const colorName = capitalize(colorTranslation.name); + const speciesName = capitalize(speciesTranslation.name); + const pose = getPoseFromPetState(petState); + + await logToDiscord({ + embeds: [ + { + title: `🛠 ${colorName} ${speciesName}`, + thumbnail: { + url: `http://pets.neopets.com/cp/${ + petType.basicImageHash || petType.imageHash + }/1/6.png`, + height: 150, + width: 150, + }, + fields: [ + { + name: `Appearance ${petState.id}, Layer ${layerId} (${zoneTranslation.label}): Known glitches`, + value: `${oldSwfAsset.knownGlitches || ""} → **${ + newKnownGlitchesString || "" + }**`, + }, + ], + timestamp: new Date().toISOString(), + url: `https://impress-2020.openneo.net/outfits/new?species=${petType.speciesId}&color=${petType.colorId}&pose=${pose}&state=${petState.id}`, + }, + ], + }); + } else { + console.error( + `Error building Discord support log: unexpected parent type ${parentType}` + ); + } } catch (e) { console.error("Error sending Discord support log", e); }