diff --git a/src/app/ItemPageLayout.js b/src/app/ItemPageLayout.js index a82b0d4..9caad8c 100644 --- a/src/app/ItemPageLayout.js +++ b/src/app/ItemPageLayout.js @@ -10,10 +10,13 @@ import { Portal, Select, Skeleton, + Spinner, Tooltip, + useToast, VStack, } from "@chakra-ui/react"; import { ExternalLinkIcon, ChevronRightIcon } from "@chakra-ui/icons"; +import { gql, useMutation } from "@apollo/client"; import { ItemBadgeList, @@ -204,13 +207,32 @@ function ItemPageBadges({ item, isEmbedded }) { } function ItemKindBadgeWithSupportTools({ item }) { - const { isSupportUser } = useSupport(); + const { isSupportUser, supportSecret } = useSupport(); + const toast = useToast(); const ncRef = React.useRef(null); const isNcAutoDetectedFromRarity = item.rarityIndex === 500 || item.rarityIndex === 0; + const [mutate, { loading }] = useMutation(gql` + mutation ItemPageSupportSetIsManuallyNc( + $itemId: ID! + $isManuallyNc: Boolean! + $supportSecret: String! + ) { + setItemIsManuallyNc( + itemId: $itemId + isManuallyNc: $isManuallyNc + supportSecret: $supportSecret + ) { + id + isNc + isManuallyNc + } + } + `); + if (isSupportUser && item.rarityIndex != null && item.isManuallyNc != null) { // TODO: Could code-split this into a SupportOnly file... return ( @@ -230,6 +252,31 @@ function ItemKindBadgeWithSupportTools({ item }) { ref={ncRef} size="xs" value={item.isManuallyNc ? "true" : "false"} + onChange={(e) => { + const isManuallyNc = e.target.value === "true"; + mutate({ + variables: { + itemId: item.id, + isManuallyNc, + supportSecret, + }, + optimisticResponse: { + setItemIsManuallyNc: { + __typename: "Item", + id: item.id, + isNc: isManuallyNc || isNcAutoDetectedFromRarity, + isManuallyNc, + }, + }, + }).catch((e) => { + console.error(e); + toast({ + status: "error", + title: + "Could not set NC status for this item. Try again?", + }); + }); + }} > + {loading && } diff --git a/src/server/types/MutationsForSupport.js b/src/server/types/MutationsForSupport.js index 6e3cbfe..03be98d 100644 --- a/src/server/types/MutationsForSupport.js +++ b/src/server/types/MutationsForSupport.js @@ -37,6 +37,12 @@ const typeDefs = gql` supportSecret: String! ): Item! + setItemIsManuallyNc( + itemId: ID! + isManuallyNc: Boolean! + supportSecret: String! + ): Item! + setLayerBodyId( layerId: ID! bodyId: ID! @@ -206,6 +212,69 @@ const resolvers = { return { id: itemId }; }, + setItemIsManuallyNc: async ( + _, + { itemId, isManuallyNc, supportSecret }, + { itemLoader, itemTranslationLoader, db } + ) => { + assertSupportSecretOrThrow(supportSecret); + + const oldItem = await itemLoader.load(itemId); + + const [ + result, + ] = await db.execute( + `UPDATE items SET is_manually_nc = ? WHERE id = ? LIMIT 1`, + [isManuallyNc ? 1 : 0, itemId] + ); + + if (result.affectedRows !== 1) { + throw new Error( + `Expected to affect 1 item, but affected ${result.affectedRows}` + ); + } + + itemLoader.clear(itemId); // we changed the item, so clear it from cache + + if (process.env["SUPPORT_TOOLS_DISCORD_WEBHOOK_URL"]) { + try { + const itemTranslation = await itemTranslationLoader.load(itemId); + const oldRuleName = oldItem.isManuallyNc + ? "Manually set: Yes" + : "Auto-detect"; + const newRuleName = isManuallyNc + ? "Manually set: Yes" + : "Auto-detect"; + await logToDiscord({ + embeds: [ + { + title: `🛠 ${itemTranslation.name}`, + thumbnail: { + url: oldItem.thumbnailUrl, + height: 80, + width: 80, + }, + fields: [ + { + name: "Is NC rule", + value: `${oldRuleName} → **${newRuleName}**`, + }, + ], + timestamp: new Date().toISOString(), + url: `https://impress.openneo.net/items/${oldItem.id}`, + }, + ], + }); + } catch (e) { + console.error("Error sending Discord support log", e); + } + } else { + console.warn("No Discord support webhook provided, skipping"); + } + + return { id: itemId }; + }, + setLayerBodyId: async ( _, { layerId, bodyId, supportSecret },