Add support tools for pet layers
This commit is contained in:
parent
e8a2b8ba28
commit
b9b6667414
3 changed files with 210 additions and 80 deletions
|
@ -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({
|
|||
<ModalOverlay>
|
||||
<ModalContent>
|
||||
<ModalHeader>
|
||||
Layer {layer.id}: {item.name}
|
||||
Layer {layer.id}: {parentName}
|
||||
</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<ModalBody>
|
||||
|
@ -244,6 +249,8 @@ function AppearanceLayerSupportModal({
|
|||
SWF <ExternalLinkIcon ml="1" />
|
||||
</Button>
|
||||
<Box flex="1 1 0" />
|
||||
{item && (
|
||||
<>
|
||||
<Button
|
||||
size="xs"
|
||||
colorScheme="gray"
|
||||
|
@ -257,10 +264,14 @@ function AppearanceLayerSupportModal({
|
|||
isOpen={uploadModalIsOpen}
|
||||
onClose={() => setUploadModalIsOpen(false)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</HStack>
|
||||
</MetadataValue>
|
||||
</Metadata>
|
||||
<Box height="8" />
|
||||
{item && (
|
||||
<>
|
||||
<AppearanceLayerSupportPetCompatibilityFields
|
||||
item={item}
|
||||
layer={layer}
|
||||
|
@ -271,18 +282,22 @@ function AppearanceLayerSupportModal({
|
|||
onChangePreviewBiology={setPreviewBiology}
|
||||
/>
|
||||
<Box height="8" />
|
||||
</>
|
||||
)}
|
||||
<AppearanceLayerSupportKnownGlitchesFields
|
||||
selectedKnownGlitches={selectedKnownGlitches}
|
||||
onChange={setSelectedKnownGlitches}
|
||||
/>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
{item && (
|
||||
<AppearanceLayerSupportModalRemoveButton
|
||||
item={item}
|
||||
layer={layer}
|
||||
outfitState={outfitState}
|
||||
onRemoveSuccess={onClose}
|
||||
/>
|
||||
)}
|
||||
<Box flex="1 0 0" />
|
||||
{mutationError && (
|
||||
<Box
|
||||
|
@ -298,7 +313,11 @@ function AppearanceLayerSupportModal({
|
|||
<Button
|
||||
isLoading={mutationLoading}
|
||||
colorScheme="green"
|
||||
onClick={mutate}
|
||||
onClick={() =>
|
||||
mutate().catch((e) => {
|
||||
/* Discard errors here; we'll show them in the UI! */
|
||||
})
|
||||
}
|
||||
flex="0 0 auto"
|
||||
>
|
||||
Save changes
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
</MetadataValue>
|
||||
<MetadataLabel>Zones:</MetadataLabel>
|
||||
<MetadataLabel>Layers:</MetadataLabel>
|
||||
<MetadataValue>
|
||||
<Wrap spacing="1">
|
||||
{currentPetAppearance.layers
|
||||
.map((l) => l.zone)
|
||||
.map((z) => `${z.label} (${z.id})`)
|
||||
.sort()
|
||||
.join(", ")}
|
||||
.map((layer) => [`${layer.zone.label} (${layer.zone.id})`, layer])
|
||||
.sort((a, b) => a[0].localeCompare(b[0]))
|
||||
.map(([text, layer]) => (
|
||||
<WrapItem>
|
||||
<PetLayerSupportLink
|
||||
outfitState={{ speciesId, colorId, pose }}
|
||||
petAppearance={currentPetAppearance}
|
||||
layer={layer}
|
||||
>
|
||||
{text}
|
||||
<EditIcon marginLeft="1" />
|
||||
</PetLayerSupportLink>
|
||||
</WrapItem>
|
||||
))}
|
||||
</Wrap>
|
||||
</MetadataValue>
|
||||
</Metadata>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function PetLayerSupportLink({ outfitState, petAppearance, layer, children }) {
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
return (
|
||||
<>
|
||||
<Button size="xs" onClick={onOpen}>
|
||||
{children}
|
||||
</Button>
|
||||
<AppearanceLayerSupportModal
|
||||
outfitState={outfitState}
|
||||
petAppearance={petAppearance}
|
||||
layer={layer}
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function PosePickerSupportNavigator({
|
||||
petAppearances,
|
||||
currentPetAppearance,
|
||||
|
|
|
@ -412,6 +412,10 @@ const resolvers = {
|
|||
itemTranslationLoader,
|
||||
swfAssetLoader,
|
||||
zoneTranslationLoader,
|
||||
petStateLoader,
|
||||
petTypeLoader,
|
||||
colorTranslationLoader,
|
||||
speciesTranslationLoader,
|
||||
db,
|
||||
}
|
||||
) => {
|
||||
|
@ -453,17 +457,18 @@ 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]));
|
||||
|
||||
if (parentType === "Item") {
|
||||
const [item, itemTranslation, zoneTranslation] = await Promise.all([
|
||||
itemLoader.load(itemId),
|
||||
itemTranslationLoader.load(itemId),
|
||||
itemLoader.load(parentId),
|
||||
itemTranslationLoader.load(parentId),
|
||||
zoneTranslationLoader.load(oldSwfAsset.zoneId),
|
||||
]);
|
||||
|
||||
|
@ -487,10 +492,56 @@ const resolvers = {
|
|||
},
|
||||
],
|
||||
timestamp: new Date().toISOString(),
|
||||
url: `https://impress.openneo.net/items/${itemId}`,
|
||||
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 || "<none>"} → **${
|
||||
newKnownGlitchesString || "<none>"
|
||||
}**`,
|
||||
},
|
||||
],
|
||||
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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue