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,
|
||||
WrapItem,
|
||||
} from "@chakra-ui/react";
|
||||
import { gql, useQuery } from "@apollo/client";
|
||||
import { gql, useMutation, useQuery } from "@apollo/client";
|
||||
import {
|
||||
appearanceLayerFragment,
|
||||
itemAppearanceFragment,
|
||||
|
@ -26,6 +26,7 @@ import HangerSpinner from "../../components/HangerSpinner";
|
|||
import { ErrorMessage, useCommonStyles } from "../../util";
|
||||
import ItemSupportAppearanceLayer from "./ItemSupportAppearanceLayer";
|
||||
import { EditIcon } from "@chakra-ui/icons";
|
||||
import useSupport from "./useSupport";
|
||||
|
||||
function AllItemLayersSupportModal({ item, isOpen, onClose }) {
|
||||
const [bulkAddProposal, setBulkAddProposal] = React.useState(null);
|
||||
|
@ -54,6 +55,7 @@ function AllItemLayersSupportModal({ item, isOpen, onClose }) {
|
|||
<AllItemLayersSupportModalContent
|
||||
item={item}
|
||||
bulkAddProposal={bulkAddProposal}
|
||||
onBulkAddComplete={() => setBulkAddProposal(null)}
|
||||
/>
|
||||
</ModalBody>
|
||||
</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(
|
||||
gql`
|
||||
query AllItemLayersSupportModal($itemId: ID!) {
|
||||
item(id: $itemId) {
|
||||
id
|
||||
allAppearances {
|
||||
id
|
||||
body {
|
||||
id
|
||||
representsAllBodies
|
||||
canonicalAppearance {
|
||||
id
|
||||
species {
|
||||
id
|
||||
name
|
||||
}
|
||||
color {
|
||||
id
|
||||
name
|
||||
isStandard
|
||||
}
|
||||
pose
|
||||
...PetAppearanceForOutfitPreview
|
||||
}
|
||||
}
|
||||
...ItemAppearanceForOutfitPreview
|
||||
}
|
||||
...AllAppearancesForItem
|
||||
}
|
||||
}
|
||||
|
||||
${itemAppearanceFragment}
|
||||
${petAppearanceFragment}
|
||||
${allAppearancesFragment}
|
||||
`,
|
||||
{ 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) {
|
||||
return (
|
||||
<Flex align="center" justify="center" minHeight="64">
|
||||
|
@ -266,7 +304,37 @@ function AllItemLayersSupportModalContent({ item, bulkAddProposal }) {
|
|||
<Flex align="center" marginBottom="6">
|
||||
<Heading size="md">Previewing bulk-add changes</Heading>
|
||||
<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
|
||||
</Button>
|
||||
</Flex>
|
||||
|
|
|
@ -55,6 +55,12 @@ const typeDefs = gql`
|
|||
supportSecret: String!
|
||||
): AppearanceLayer
|
||||
|
||||
bulkAddLayersToItem(
|
||||
itemId: ID!
|
||||
entries: [BulkAddLayersToItemEntry!]!
|
||||
supportSecret: String!
|
||||
): Item
|
||||
|
||||
removeLayerFromItem(
|
||||
layerId: ID!
|
||||
itemId: ID!
|
||||
|
@ -75,6 +81,11 @@ const typeDefs = gql`
|
|||
|
||||
setUsername(userId: ID!, newUsername: String!, supportSecret: String!): User
|
||||
}
|
||||
|
||||
input BulkAddLayersToItemEntry {
|
||||
layerId: ID!
|
||||
bodyId: ID!
|
||||
}
|
||||
`;
|
||||
|
||||
const resolvers = {
|
||||
|
@ -479,6 +490,80 @@ const resolvers = {
|
|||
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 (
|
||||
_,
|
||||
{ layerId, itemId, supportSecret },
|
||||
|
|
Loading…
Reference in a new issue