From 240a683e719d7f57d76d5a29dcba2f9fc205c48d Mon Sep 17 00:00:00 2001 From: Matchu Date: Mon, 15 Aug 2022 23:57:33 -0700 Subject: [PATCH] Finish wiring up ~owls data Hey nice it looks like it's working! :3 "Bright Speckled Parasol" is a nice test case, it has a long text string! And when the NC value is not included in the ~owls list, we indeed don't show the badge! --- pages/api/allNCTradeValues.js | 14 +++-- src/app/ItemPage.js | 12 +++- src/app/ItemPageLayout.js | 102 +++------------------------------- src/app/ItemTradesPage.js | 2 +- src/server/loaders.js | 27 +++++++++ src/server/types/Item.js | 26 +++++++++ 6 files changed, 81 insertions(+), 102 deletions(-) diff --git a/pages/api/allNCTradeValues.js b/pages/api/allNCTradeValues.js index 06381e5..3112776 100644 --- a/pages/api/allNCTradeValues.js +++ b/pages/api/allNCTradeValues.js @@ -79,20 +79,26 @@ async function loadOWLSValuesByIdOrName() { } const itemValuesByIdOrName = {}; - for (const [itemName, value] of Object.entries(json)) { + for (const [itemName, valueText] of Object.entries(json)) { // OWLS returns an empty string for NC Mall items they don't have a trade // value for, to allow the script to distinguish between NP items vs // no-data NC items. We omit it from our data instead, because our UI is // already aware of whether the item is NP or NC. - if (value.trim() === "") { + if (valueText.trim() === "") { continue; } // TODO: OWLS doesn't currently provide item IDs ever. Add support for it // if it does! (I'm keeping the rest of the code the same because I - // think that might happen for disambiguation, like Waka did.) + // think that might happen for disambiguation, like Waka did.) Until + // then, we just always key by name. const normalizedItemName = normalizeItemName(itemName); - itemValuesByIdOrName[normalizedItemName] = value; + + // We wrap it in an object with the key `valueText`, just to not break + // potential external consumers of this endpoint if we add more fields. + // (This is kinda silly and unnecessary, but it should get gzipped out and + // shouldn't add substantial time to building or parsing, so like w/e!) + itemValuesByIdOrName[normalizedItemName] = { valueText }; } return itemValuesByIdOrName; diff --git a/src/app/ItemPage.js b/src/app/ItemPage.js index e759010..ae6e6b7 100644 --- a/src/app/ItemPage.js +++ b/src/app/ItemPage.js @@ -36,7 +36,13 @@ import { useQuery, useMutation } from "@apollo/client"; import { Link, useParams } from "react-router-dom"; import ItemPageLayout, { SubtleSkeleton } from "./ItemPageLayout"; -import { Delay, logAndCapture, useLocalStorage, usePageTitle } from "./util"; +import { + Delay, + logAndCapture, + MajorErrorMessage, + useLocalStorage, + usePageTitle, +} from "./util"; import HTML5Badge, { layerUsesHTML5 } from "./components/HTML5Badge"; import { itemAppearanceFragment, @@ -77,7 +83,7 @@ export function ItemPageContent({ itemId, isEmbedded }) { thumbnailUrl description createdAt - wakaValueText + ncTradeValueText # For Support users. rarityIndex @@ -91,7 +97,7 @@ export function ItemPageContent({ itemId, isEmbedded }) { usePageTitle(data?.item?.name, { skip: isEmbedded }); if (error) { - return {error.message}; + return ; } const item = data?.item; diff --git a/src/app/ItemPageLayout.js b/src/app/ItemPageLayout.js index 562af96..e393a89 100644 --- a/src/app/ItemPageLayout.js +++ b/src/app/ItemPageLayout.js @@ -5,7 +5,6 @@ import { Flex, Popover, PopoverArrow, - PopoverBody, PopoverContent, PopoverTrigger, Portal, @@ -16,12 +15,7 @@ import { useToast, VStack, } from "@chakra-ui/react"; -import { - ExternalLinkIcon, - ChevronRightIcon, - QuestionIcon, - WarningTwoIcon, -} from "@chakra-ui/icons"; +import { ExternalLinkIcon, ChevronRightIcon } from "@chakra-ui/icons"; import { gql, useMutation } from "@apollo/client"; import { @@ -159,44 +153,15 @@ function ItemPageBadges({ item, isEmbedded }) { {item.isNc && ( - {shouldShowWaka() && item.wakaValueText && ( - <> - {/* For hover-y devices, use a hover popover over the badge. */} - - - - - Waka: {item.wakaValueText} - - - - {/* For touch-y devices, use a tappable help icon. */} - - - - Waka: {item.wakaValueText} - - - - - - - - + {item.ncTradeValueText && ( + + OWLS: {item.ncTradeValueText} + )} )} @@ -439,55 +404,4 @@ function ShortTimestamp({ when }) { ); } -function WakaPopover({ children, ...props }) { - return ( - - {children} - - - - -

- - The Waka Guide for NC trade values is closing down! - {" "} - We're sad to see them go, but excited that the team gets to move - onto the next phase in their life 💖 -

- -

- The Waka guide was last updated August 7, 2021, so this value - might not be accurate anymore. Consider checking in with the - Neoboards! -

- -

- Thanks again to the Waka team for letting us experiment with - sharing their trade values here. Best wishes for everything to - come! 💜 -

-
-
-
-
- ); -} - -// August 21, 2021. (The month uses 0-indexing, but nothing else does! 🙃) -const STOP_SHOWING_WAKA_AFTER = new Date(2021, 7, 21, 0, 0, 0, 0); - -/** - * shouldShowWaka returns true if, according to the browser, it's not yet - * August 21, 2021. It starts returning false at midnight on Aug 21. - * - * That way, our Waka deprecation message is on an auto-timer. After Aug 21, - * it's safe to remove all Waka UI code, and the Waka API endpoint and GraphQL - * fields. (It might be kind to return a placeholder string for the GraphQL - * case!) - */ -function shouldShowWaka() { - const now = new Date(); - return now < STOP_SHOWING_WAKA_AFTER; -} - export default ItemPageLayout; diff --git a/src/app/ItemTradesPage.js b/src/app/ItemTradesPage.js index 953956f..c3c8359 100644 --- a/src/app/ItemTradesPage.js +++ b/src/app/ItemTradesPage.js @@ -102,7 +102,7 @@ function ItemTradesPage({ thumbnailUrl description createdAt - wakaValueText + ncTradeValueText } } `, diff --git a/src/server/loaders.js b/src/server/loaders.js index 44703ca..133a567 100644 --- a/src/server/loaders.js +++ b/src/server/loaders.js @@ -1,4 +1,5 @@ import DataLoader from "dataloader"; +import fetch from "node-fetch"; import { normalizeRow } from "./util"; const buildClosetListLoader = (db) => @@ -825,6 +826,31 @@ const buildItemTradesLoader = (db, loaders) => { cacheKeyFn: ({ itemId, isOwned }) => `${itemId}-${isOwned}` } ); +const buildItemNCTradeValueLoader = () => + new DataLoader(async (itemIds) => { + // This loader calls our /api/allNCTradeValues endpoint, to take advantage + // of the CDN caching. This helps us respond a bit faster than calling the + // API directly would, and avoids putting network pressure or caching + // complexity on our ~owls friends! (It would also be pretty reasonable to + // do this as a process-level cache or something instead, but I'm reusing + // Waka code from when we were on a more distributed system where that + // wouldn't have worked out, and I don't think the effort to refactor this + // just for the potential perf win is worthy!) + const url = process.env.NODE_ENV === "production" + ? "https://impress-2020.openneo.net/api/allNCTradeValues" + : "http://localhost:3000/api/allNCTradeValues"; + const res = await fetch(url); + if (!res.ok) { + throw new Error( + `Error loading /api/allNCTradeValues: ${res.status} ${res.statusText}` + ); + } + + const allNCTradeValues = await res.json(); + + return itemIds.map((itemId) => allNCTradeValues[itemId]); + }); + const buildPetTypeLoader = (db, loaders) => new DataLoader(async (petTypeIds) => { const qs = petTypeIds.map((_) => "?").join(","); @@ -1495,6 +1521,7 @@ function buildLoaders(db) { db ); loaders.itemTradesLoader = buildItemTradesLoader(db, loaders); + loaders.itemNCTradeValueLoader = buildItemNCTradeValueLoader(); loaders.petTypeLoader = buildPetTypeLoader(db, loaders); loaders.petTypeBySpeciesAndColorLoader = buildPetTypeBySpeciesAndColorLoader( db, diff --git a/src/server/types/Item.js b/src/server/types/Item.js index 343ec21..8ba162f 100644 --- a/src/server/types/Item.js +++ b/src/server/types/Item.js @@ -33,6 +33,18 @@ const typeDefs = gql` """ wakaValueText: String @cacheControl(maxAge: ${oneHour}) + """ + This item's NC trade value as a human-readable string. Returns null if the + value is not known. + + Note that the format of this string is not well-specified—it's fully + human-curated and may include surprising words or extra notes! We recommend + presenting the text exactly as-is, rather than trying to parse and math it. + + This data is currently curated by neopets.com/~owls, thank you!! <3 + """ + ncTradeValueText: String @cacheControl(maxAge: ${oneHour}) + currentUserOwnsThis: Boolean! @cacheControl(maxAge: 0, scope: PRIVATE) currentUserWantsThis: Boolean! @cacheControl(maxAge: 0, scope: PRIVATE) @@ -344,6 +356,20 @@ const resolvers = { // This feature is deprecated, so now we just always return unknown value. return null; }, + ncTradeValueText: async ({ id }, _, { itemNCTradeValueLoader }) => { + let ncTradeValue; + try { + ncTradeValue = await itemNCTradeValueLoader.load(id); + } catch (e) { + console.error( + `Error loading ncTradeValueText for item ${id}, skipping:` + ); + console.error(e); + ncTradeValue = null; + } + + return ncTradeValue ? ncTradeValue.valueText : null; + }, currentUserOwnsThis: async ( { id },