Bulk-add tool actually saves stuff!
I fixed Dug Up Dirt foreground, hooray! Hope it sticks
This commit is contained in:
parent
5f32d80022
commit
4e9805af60
2 changed files with 180 additions and 27 deletions
|
@ -16,7 +16,7 @@ import {
|
||||||
Wrap,
|
Wrap,
|
||||||
WrapItem,
|
WrapItem,
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import { gql, useQuery } from "@apollo/client";
|
import { gql, useMutation, useQuery } from "@apollo/client";
|
||||||
import {
|
import {
|
||||||
appearanceLayerFragment,
|
appearanceLayerFragment,
|
||||||
itemAppearanceFragment,
|
itemAppearanceFragment,
|
||||||
|
@ -26,6 +26,7 @@ import HangerSpinner from "../../components/HangerSpinner";
|
||||||
import { ErrorMessage, useCommonStyles } from "../../util";
|
import { ErrorMessage, useCommonStyles } from "../../util";
|
||||||
import ItemSupportAppearanceLayer from "./ItemSupportAppearanceLayer";
|
import ItemSupportAppearanceLayer from "./ItemSupportAppearanceLayer";
|
||||||
import { EditIcon } from "@chakra-ui/icons";
|
import { EditIcon } from "@chakra-ui/icons";
|
||||||
|
import useSupport from "./useSupport";
|
||||||
|
|
||||||
function AllItemLayersSupportModal({ item, isOpen, onClose }) {
|
function AllItemLayersSupportModal({ item, isOpen, onClose }) {
|
||||||
const [bulkAddProposal, setBulkAddProposal] = React.useState(null);
|
const [bulkAddProposal, setBulkAddProposal] = React.useState(null);
|
||||||
|
@ -54,6 +55,7 @@ function AllItemLayersSupportModal({ item, isOpen, onClose }) {
|
||||||
<AllItemLayersSupportModalContent
|
<AllItemLayersSupportModalContent
|
||||||
item={item}
|
item={item}
|
||||||
bulkAddProposal={bulkAddProposal}
|
bulkAddProposal={bulkAddProposal}
|
||||||
|
onBulkAddComplete={() => setBulkAddProposal(null)}
|
||||||
/>
|
/>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
@ -149,39 +151,53 @@ function BulkAddBodySpecificAssetsForm({ bulkAddProposal, onSubmit }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function AllItemLayersSupportModalContent({ item, bulkAddProposal }) {
|
const allAppearancesFragment = gql`
|
||||||
|
fragment AllAppearancesForItem on Item {
|
||||||
|
allAppearances {
|
||||||
|
id
|
||||||
|
body {
|
||||||
|
id
|
||||||
|
representsAllBodies
|
||||||
|
canonicalAppearance {
|
||||||
|
id
|
||||||
|
species {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
color {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
isStandard
|
||||||
|
}
|
||||||
|
pose
|
||||||
|
...PetAppearanceForOutfitPreview
|
||||||
|
}
|
||||||
|
}
|
||||||
|
...ItemAppearanceForOutfitPreview
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
${itemAppearanceFragment}
|
||||||
|
${petAppearanceFragment}
|
||||||
|
`;
|
||||||
|
|
||||||
|
function AllItemLayersSupportModalContent({
|
||||||
|
item,
|
||||||
|
bulkAddProposal,
|
||||||
|
onBulkAddComplete,
|
||||||
|
}) {
|
||||||
|
const { supportSecret } = useSupport();
|
||||||
|
|
||||||
const { loading, error, data } = useQuery(
|
const { loading, error, data } = useQuery(
|
||||||
gql`
|
gql`
|
||||||
query AllItemLayersSupportModal($itemId: ID!) {
|
query AllItemLayersSupportModal($itemId: ID!) {
|
||||||
item(id: $itemId) {
|
item(id: $itemId) {
|
||||||
id
|
id
|
||||||
allAppearances {
|
...AllAppearancesForItem
|
||||||
id
|
|
||||||
body {
|
|
||||||
id
|
|
||||||
representsAllBodies
|
|
||||||
canonicalAppearance {
|
|
||||||
id
|
|
||||||
species {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
}
|
|
||||||
color {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
isStandard
|
|
||||||
}
|
|
||||||
pose
|
|
||||||
...PetAppearanceForOutfitPreview
|
|
||||||
}
|
|
||||||
}
|
|
||||||
...ItemAppearanceForOutfitPreview
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
${itemAppearanceFragment}
|
${allAppearancesFragment}
|
||||||
${petAppearanceFragment}
|
|
||||||
`,
|
`,
|
||||||
{ variables: { itemId: item.id } }
|
{ variables: { itemId: item.id } }
|
||||||
);
|
);
|
||||||
|
@ -236,6 +252,28 @@ function AllItemLayersSupportModalContent({ item, bulkAddProposal }) {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [
|
||||||
|
sendBulkAddMutation,
|
||||||
|
{ loading: mutationLoading, error: mutationError },
|
||||||
|
] = useMutation(gql`
|
||||||
|
mutation AllItemLayersSupportModal_BulkAddMutation(
|
||||||
|
$itemId: ID!
|
||||||
|
$entries: [BulkAddLayersToItemEntry!]!
|
||||||
|
$supportSecret: String!
|
||||||
|
) {
|
||||||
|
bulkAddLayersToItem(
|
||||||
|
itemId: $itemId
|
||||||
|
entries: $entries
|
||||||
|
supportSecret: $supportSecret
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
...AllAppearancesForItem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
${allAppearancesFragment}
|
||||||
|
`);
|
||||||
|
|
||||||
if (loading || loading2) {
|
if (loading || loading2) {
|
||||||
return (
|
return (
|
||||||
<Flex align="center" justify="center" minHeight="64">
|
<Flex align="center" justify="center" minHeight="64">
|
||||||
|
@ -266,7 +304,37 @@ function AllItemLayersSupportModalContent({ item, bulkAddProposal }) {
|
||||||
<Flex align="center" marginBottom="6">
|
<Flex align="center" marginBottom="6">
|
||||||
<Heading size="md">Previewing bulk-add changes</Heading>
|
<Heading size="md">Previewing bulk-add changes</Heading>
|
||||||
<Box flex="1 0 auto" width="4" />
|
<Box flex="1 0 auto" width="4" />
|
||||||
<Button size="sm" colorScheme="green">
|
{mutationError && (
|
||||||
|
<ErrorMessage fontSize="xs" textAlign="right" marginRight="2">
|
||||||
|
{mutationError.message}
|
||||||
|
</ErrorMessage>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
flex="0 0 auto"
|
||||||
|
size="sm"
|
||||||
|
colorScheme="green"
|
||||||
|
isLoading={mutationLoading}
|
||||||
|
onClick={() => {
|
||||||
|
// HACK: This could pick up not just new layers, but existing layers
|
||||||
|
// that aren't changing. Shouldn't be a problem to save,
|
||||||
|
// though?
|
||||||
|
// NOTE: This API uses actual layer IDs, instead of the remote IDs
|
||||||
|
// that we use for body assignment in most of this tool.
|
||||||
|
const entries = itemAppearances
|
||||||
|
.map((a) =>
|
||||||
|
a.layers.map((l) => ({ layerId: l.id, bodyId: a.body.id }))
|
||||||
|
)
|
||||||
|
.flat();
|
||||||
|
|
||||||
|
sendBulkAddMutation({
|
||||||
|
variables: { itemId: item.id, entries, supportSecret },
|
||||||
|
})
|
||||||
|
.then(onBulkAddComplete)
|
||||||
|
.catch((e) => {
|
||||||
|
/* Handled in UI */
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
Save {bulkAddProposalData.layersToAdd.length} changes
|
Save {bulkAddProposalData.layersToAdd.length} changes
|
||||||
</Button>
|
</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
|
@ -55,6 +55,12 @@ const typeDefs = gql`
|
||||||
supportSecret: String!
|
supportSecret: String!
|
||||||
): AppearanceLayer
|
): AppearanceLayer
|
||||||
|
|
||||||
|
bulkAddLayersToItem(
|
||||||
|
itemId: ID!
|
||||||
|
entries: [BulkAddLayersToItemEntry!]!
|
||||||
|
supportSecret: String!
|
||||||
|
): Item
|
||||||
|
|
||||||
removeLayerFromItem(
|
removeLayerFromItem(
|
||||||
layerId: ID!
|
layerId: ID!
|
||||||
itemId: ID!
|
itemId: ID!
|
||||||
|
@ -75,6 +81,11 @@ const typeDefs = gql`
|
||||||
|
|
||||||
setUsername(userId: ID!, newUsername: String!, supportSecret: String!): User
|
setUsername(userId: ID!, newUsername: String!, supportSecret: String!): User
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input BulkAddLayersToItemEntry {
|
||||||
|
layerId: ID!
|
||||||
|
bodyId: ID!
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const resolvers = {
|
const resolvers = {
|
||||||
|
@ -479,6 +490,80 @@ const resolvers = {
|
||||||
return { id: layerId };
|
return { id: layerId };
|
||||||
},
|
},
|
||||||
|
|
||||||
|
bulkAddLayersToItem: async (
|
||||||
|
_,
|
||||||
|
{ itemId, entries, supportSecret },
|
||||||
|
{ itemLoader, itemTranslationLoader, db }
|
||||||
|
) => {
|
||||||
|
assertSupportSecretOrThrow(supportSecret);
|
||||||
|
|
||||||
|
const item = await itemLoader.load(itemId);
|
||||||
|
if (!item) {
|
||||||
|
console.warn(
|
||||||
|
`Skipping removeLayerFromItem for unknown item ID: ${itemId}`
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEntry = async ({ layerId, bodyId }) => {
|
||||||
|
await Promise.all([
|
||||||
|
db.execute(`UPDATE swf_assets SET body_id = ? WHERE id = ? LIMIT 1`, [
|
||||||
|
bodyId,
|
||||||
|
layerId,
|
||||||
|
]),
|
||||||
|
db.execute(
|
||||||
|
// Technique adapted from https://stackoverflow.com/a/3025332/107415
|
||||||
|
`
|
||||||
|
INSERT INTO parents_swf_assets
|
||||||
|
(parent_type, parent_id, swf_asset_id)
|
||||||
|
SELECT "Item", ?, ? FROM DUAL
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT * FROM parents_swf_assets
|
||||||
|
WHERE parent_type = "Item" AND parent_id = ? AND
|
||||||
|
swf_asset_id = ?
|
||||||
|
)
|
||||||
|
`,
|
||||||
|
[itemId, layerId, itemId, layerId]
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
await Promise.all(entries.map(handleEntry));
|
||||||
|
|
||||||
|
if (process.env["SUPPORT_TOOLS_DISCORD_WEBHOOK_URL"]) {
|
||||||
|
try {
|
||||||
|
const itemTranslation = await itemTranslationLoader.load(itemId);
|
||||||
|
|
||||||
|
await logToDiscord({
|
||||||
|
embeds: [
|
||||||
|
{
|
||||||
|
title: `🛠 ${itemTranslation.name}`,
|
||||||
|
thumbnail: {
|
||||||
|
url: item.thumbnailUrl,
|
||||||
|
height: 80,
|
||||||
|
width: 80,
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: `Bulk-add body-specific layers`,
|
||||||
|
value: `✅ Added/updated ${entries.length} layers`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
url: `https://impress.openneo.net/items/${itemId}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error sending Discord support log", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn("No Discord support webhook provided, skipping");
|
||||||
|
}
|
||||||
|
|
||||||
|
return { id: itemId };
|
||||||
|
},
|
||||||
|
|
||||||
removeLayerFromItem: async (
|
removeLayerFromItem: async (
|
||||||
_,
|
_,
|
||||||
{ layerId, itemId, supportSecret },
|
{ layerId, itemId, supportSecret },
|
||||||
|
|
Loading…
Reference in a new issue