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!
This commit is contained in:
parent
16b86fc65e
commit
240a683e71
6 changed files with 81 additions and 102 deletions
|
@ -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;
|
||||
|
|
|
@ -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 <Box color="red.400">{error.message}</Box>;
|
||||
return <MajorErrorMessage error={error} />;
|
||||
}
|
||||
|
||||
const item = data?.item;
|
||||
|
|
|
@ -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 && (
|
||||
<SubtleSkeleton
|
||||
isLoaded={
|
||||
// Distinguish between undefined (still loading) and null (loaded and
|
||||
// empty).
|
||||
item.wakaValueText !== undefined
|
||||
// Distinguish between undefined (still loading) and null (loaded
|
||||
// and empty).
|
||||
item.ncTradeValueText !== undefined
|
||||
}
|
||||
>
|
||||
{shouldShowWaka() && item.wakaValueText && (
|
||||
<>
|
||||
{/* For hover-y devices, use a hover popover over the badge. */}
|
||||
<Box sx={{ "@media (hover: none)": { display: "none" } }}>
|
||||
<WakaPopover trigger="hover">
|
||||
<LinkBadge
|
||||
href="http://www.neopets.com/~waka"
|
||||
colorScheme="yellow"
|
||||
>
|
||||
<WarningTwoIcon marginRight="1" />
|
||||
Waka: {item.wakaValueText}
|
||||
{item.ncTradeValueText && (
|
||||
<LinkBadge href="http://www.neopets.com/~owls">
|
||||
OWLS: {item.ncTradeValueText}
|
||||
</LinkBadge>
|
||||
</WakaPopover>
|
||||
</Box>
|
||||
{/* For touch-y devices, use a tappable help icon. */}
|
||||
<Flex
|
||||
sx={{ "@media (hover: hover)": { display: "none" } }}
|
||||
align="center"
|
||||
>
|
||||
<LinkBadge
|
||||
href="http://www.neopets.com/~waka"
|
||||
colorScheme="yellow"
|
||||
>
|
||||
<WarningTwoIcon marginRight="1" />
|
||||
Waka: {item.wakaValueText}
|
||||
</LinkBadge>
|
||||
<WakaPopover>
|
||||
<Flex align="center" fontSize="sm" paddingX="2" tabIndex="0">
|
||||
<QuestionIcon />
|
||||
</Flex>
|
||||
</WakaPopover>
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
</SubtleSkeleton>
|
||||
)}
|
||||
|
@ -439,55 +404,4 @@ function ShortTimestamp({ when }) {
|
|||
);
|
||||
}
|
||||
|
||||
function WakaPopover({ children, ...props }) {
|
||||
return (
|
||||
<Popover placement="bottom" {...props}>
|
||||
<PopoverTrigger>{children}</PopoverTrigger>
|
||||
<Portal>
|
||||
<PopoverContent>
|
||||
<PopoverArrow />
|
||||
<PopoverBody fontSize="sm">
|
||||
<p>
|
||||
<strong>
|
||||
The Waka Guide for NC trade values is closing down!
|
||||
</strong>{" "}
|
||||
We're sad to see them go, but excited that the team gets to move
|
||||
onto the next phase in their life 💖
|
||||
</p>
|
||||
<Box height="1em" />
|
||||
<p>
|
||||
The Waka guide was last updated August 7, 2021, so this value
|
||||
might not be accurate anymore. Consider checking in with the
|
||||
Neoboards!
|
||||
</p>
|
||||
<Box height="1em" />
|
||||
<p>
|
||||
Thanks again to the Waka team for letting us experiment with
|
||||
sharing their trade values here. Best wishes for everything to
|
||||
come! 💜
|
||||
</p>
|
||||
</PopoverBody>
|
||||
</PopoverContent>
|
||||
</Portal>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
|
|
@ -102,7 +102,7 @@ function ItemTradesPage({
|
|||
thumbnailUrl
|
||||
description
|
||||
createdAt
|
||||
wakaValueText
|
||||
ncTradeValueText
|
||||
}
|
||||
}
|
||||
`,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 },
|
||||
|
|
Loading…
Reference in a new issue