2020-11-23 09:34:46 -08:00
|
|
|
import React from "react";
|
2021-02-22 19:11:03 -08:00
|
|
|
import {
|
|
|
|
Badge,
|
|
|
|
Box,
|
|
|
|
Flex,
|
|
|
|
Popover,
|
|
|
|
PopoverArrow,
|
2021-04-07 16:48:41 -07:00
|
|
|
PopoverBody,
|
2021-02-22 19:11:03 -08:00
|
|
|
PopoverContent,
|
|
|
|
PopoverTrigger,
|
|
|
|
Portal,
|
|
|
|
Select,
|
|
|
|
Skeleton,
|
2021-02-22 19:37:24 -08:00
|
|
|
Spinner,
|
2021-02-22 19:11:03 -08:00
|
|
|
Tooltip,
|
2021-02-22 19:37:24 -08:00
|
|
|
useToast,
|
2021-02-22 19:11:03 -08:00
|
|
|
VStack,
|
|
|
|
} from "@chakra-ui/react";
|
2021-04-07 16:48:41 -07:00
|
|
|
import {
|
|
|
|
ExternalLinkIcon,
|
|
|
|
ChevronRightIcon,
|
|
|
|
QuestionIcon,
|
2021-08-07 21:32:22 -07:00
|
|
|
WarningTwoIcon,
|
2021-04-07 16:48:41 -07:00
|
|
|
} from "@chakra-ui/icons";
|
2021-02-22 19:37:24 -08:00
|
|
|
import { gql, useMutation } from "@apollo/client";
|
2020-11-23 09:34:46 -08:00
|
|
|
|
|
|
|
import {
|
|
|
|
ItemBadgeList,
|
|
|
|
ItemKindBadge,
|
|
|
|
ItemThumbnail,
|
|
|
|
} from "./components/ItemCard";
|
|
|
|
import { Heading1 } from "./util";
|
|
|
|
|
2021-02-22 19:11:03 -08:00
|
|
|
import useSupport from "./WardrobePage/support/useSupport";
|
|
|
|
|
2020-11-23 09:34:46 -08:00
|
|
|
function ItemPageLayout({ children, item, isEmbedded }) {
|
|
|
|
return (
|
|
|
|
<Box>
|
|
|
|
<ItemPageHeader item={item} isEmbedded={isEmbedded} />
|
|
|
|
<Box>{children}</Box>
|
|
|
|
</Box>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function ItemPageHeader({ item, isEmbedded }) {
|
|
|
|
return (
|
|
|
|
<Box
|
|
|
|
display="flex"
|
|
|
|
alignItems="center"
|
|
|
|
justifyContent="flex-start"
|
|
|
|
width="100%"
|
|
|
|
>
|
|
|
|
<SubtleSkeleton isLoaded={item?.thumbnailUrl} marginRight="4">
|
|
|
|
<ItemThumbnail item={item} size="lg" isActive flex="0 0 auto" />
|
|
|
|
</SubtleSkeleton>
|
|
|
|
<Box>
|
|
|
|
<SubtleSkeleton isLoaded={item?.name}>
|
|
|
|
<Heading1
|
|
|
|
lineHeight="1.1"
|
|
|
|
// Nudge down the size a bit in the embed case, to better fit the
|
|
|
|
// tighter layout!
|
|
|
|
size={isEmbedded ? "xl" : "2xl"}
|
|
|
|
>
|
|
|
|
{item?.name || "Item name here"}
|
|
|
|
</Heading1>
|
|
|
|
</SubtleSkeleton>
|
|
|
|
<ItemPageBadges item={item} isEmbedded={isEmbedded} />
|
|
|
|
</Box>
|
|
|
|
</Box>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* SubtleSkeleton hides the skeleton animation until a second has passed, and
|
|
|
|
* doesn't fade in the content if it loads near-instantly. This helps avoid
|
|
|
|
* flash-of-content stuff!
|
|
|
|
*
|
|
|
|
* For plain Skeletons, we often use <Delay><Skeleton /></Delay> instead. But
|
|
|
|
* that pattern doesn't work as well for wrapper skeletons where we're using
|
|
|
|
* placeholder content for layout: we don't want the delay if the content
|
|
|
|
* really _is_ present!
|
|
|
|
*/
|
|
|
|
export function SubtleSkeleton({ isLoaded, ...props }) {
|
|
|
|
const [shouldFadeIn, setShouldFadeIn] = React.useState(false);
|
|
|
|
const [shouldShowSkeleton, setShouldShowSkeleton] = React.useState(false);
|
|
|
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
const t = setTimeout(() => {
|
|
|
|
if (!isLoaded) {
|
|
|
|
setShouldFadeIn(true);
|
|
|
|
}
|
|
|
|
}, 150);
|
|
|
|
return () => clearTimeout(t);
|
|
|
|
});
|
|
|
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
const t = setTimeout(() => setShouldShowSkeleton(true), 500);
|
|
|
|
return () => clearTimeout(t);
|
|
|
|
});
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Skeleton
|
|
|
|
fadeDuration={shouldFadeIn ? undefined : 0}
|
|
|
|
startColor={shouldShowSkeleton ? undefined : "transparent"}
|
|
|
|
endColor={shouldShowSkeleton ? undefined : "transparent"}
|
|
|
|
isLoaded={isLoaded}
|
|
|
|
{...props}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function ItemPageBadges({ item, isEmbedded }) {
|
|
|
|
const searchBadgesAreLoaded = item?.name != null && item?.isNc != null;
|
|
|
|
|
|
|
|
return (
|
|
|
|
<ItemBadgeList marginTop="1">
|
|
|
|
<SubtleSkeleton isLoaded={item?.isNc != null}>
|
2021-02-22 19:11:03 -08:00
|
|
|
<ItemKindBadgeWithSupportTools item={item} />
|
2020-11-23 09:34:46 -08:00
|
|
|
</SubtleSkeleton>
|
|
|
|
{
|
|
|
|
// If the createdAt date is null (loaded and empty), hide the badge.
|
|
|
|
item.createdAt !== null && (
|
|
|
|
<SubtleSkeleton
|
|
|
|
// Distinguish between undefined (still loading) and null (loaded and
|
|
|
|
// empty).
|
|
|
|
isLoaded={item.createdAt !== undefined}
|
|
|
|
>
|
|
|
|
<Badge
|
|
|
|
display="block"
|
|
|
|
minWidth="5.25em"
|
|
|
|
boxSizing="content-box"
|
|
|
|
textAlign="center"
|
|
|
|
>
|
|
|
|
{item.createdAt && <ShortTimestamp when={item.createdAt} />}
|
|
|
|
</Badge>
|
|
|
|
</SubtleSkeleton>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
<SubtleSkeleton isLoaded={searchBadgesAreLoaded}>
|
|
|
|
<LinkBadge
|
|
|
|
href={`https://impress.openneo.net/items/${item.id}`}
|
|
|
|
isEmbedded={isEmbedded}
|
|
|
|
>
|
|
|
|
Classic DTI
|
|
|
|
</LinkBadge>
|
|
|
|
</SubtleSkeleton>
|
|
|
|
<SubtleSkeleton isLoaded={searchBadgesAreLoaded}>
|
|
|
|
<LinkBadge
|
|
|
|
href={
|
|
|
|
"https://items.jellyneo.net/search/?name=" +
|
|
|
|
encodeURIComponent(item.name) +
|
|
|
|
"&name_type=3"
|
|
|
|
}
|
|
|
|
isEmbedded={isEmbedded}
|
|
|
|
>
|
|
|
|
Jellyneo
|
|
|
|
</LinkBadge>
|
|
|
|
</SubtleSkeleton>
|
2021-04-07 16:48:41 -07:00
|
|
|
{item.isNc && (
|
|
|
|
<SubtleSkeleton
|
|
|
|
isLoaded={
|
|
|
|
// Distinguish between undefined (still loading) and null (loaded and
|
|
|
|
// empty).
|
|
|
|
item.wakaValueText !== undefined
|
|
|
|
}
|
|
|
|
>
|
2021-08-07 21:32:22 -07:00
|
|
|
{shouldShowWaka() && item.wakaValueText && (
|
2021-04-07 16:48:41 -07:00
|
|
|
<>
|
|
|
|
{/* For hover-y devices, use a hover popover over the badge. */}
|
|
|
|
<Box sx={{ "@media (hover: none)": { display: "none" } }}>
|
|
|
|
<WakaPopover trigger="hover">
|
2021-08-07 21:32:22 -07:00
|
|
|
<LinkBadge
|
|
|
|
href="http://www.neopets.com/~waka"
|
|
|
|
colorScheme="yellow"
|
|
|
|
>
|
|
|
|
<WarningTwoIcon marginRight="1" />
|
2021-04-07 16:48:41 -07:00
|
|
|
Waka: {item.wakaValueText}
|
|
|
|
</LinkBadge>
|
|
|
|
</WakaPopover>
|
|
|
|
</Box>
|
|
|
|
{/* For touch-y devices, use a tappable help icon. */}
|
|
|
|
<Flex
|
|
|
|
sx={{ "@media (hover: hover)": { display: "none" } }}
|
|
|
|
align="center"
|
|
|
|
>
|
2021-08-07 21:32:22 -07:00
|
|
|
<LinkBadge
|
|
|
|
href="http://www.neopets.com/~waka"
|
|
|
|
colorScheme="yellow"
|
|
|
|
>
|
|
|
|
<WarningTwoIcon marginRight="1" />
|
2021-04-07 16:48:41 -07:00
|
|
|
Waka: {item.wakaValueText}
|
|
|
|
</LinkBadge>
|
|
|
|
<WakaPopover>
|
|
|
|
<Flex align="center" fontSize="sm" paddingX="2" tabIndex="0">
|
|
|
|
<QuestionIcon />
|
|
|
|
</Flex>
|
|
|
|
</WakaPopover>
|
|
|
|
</Flex>
|
|
|
|
</>
|
|
|
|
)}
|
|
|
|
</SubtleSkeleton>
|
|
|
|
)}
|
2020-11-23 09:34:46 -08:00
|
|
|
<SubtleSkeleton isLoaded={searchBadgesAreLoaded}>
|
|
|
|
{!item?.isNc && !item?.isPb && (
|
|
|
|
<LinkBadge
|
|
|
|
href={
|
2022-03-11 05:37:13 -08:00
|
|
|
"http://www.neopets.com/shops/wizard.phtml?string=" +
|
2020-11-23 09:34:46 -08:00
|
|
|
encodeURIComponent(item.name)
|
|
|
|
}
|
|
|
|
isEmbedded={isEmbedded}
|
|
|
|
>
|
|
|
|
Shop Wiz
|
|
|
|
</LinkBadge>
|
|
|
|
)}
|
|
|
|
</SubtleSkeleton>
|
|
|
|
<SubtleSkeleton isLoaded={searchBadgesAreLoaded}>
|
|
|
|
{!item?.isNc && !item?.isPb && (
|
|
|
|
<LinkBadge
|
|
|
|
href={
|
|
|
|
"http://www.neopets.com/portal/supershopwiz.phtml?string=" +
|
|
|
|
encodeURIComponent(item.name)
|
|
|
|
}
|
|
|
|
isEmbedded={isEmbedded}
|
|
|
|
>
|
|
|
|
Super Wiz
|
|
|
|
</LinkBadge>
|
|
|
|
)}
|
|
|
|
</SubtleSkeleton>
|
|
|
|
<SubtleSkeleton isLoaded={searchBadgesAreLoaded}>
|
|
|
|
{!item?.isNc && !item?.isPb && (
|
|
|
|
<LinkBadge
|
|
|
|
href={
|
|
|
|
"http://www.neopets.com/island/tradingpost.phtml?type=browse&criteria=item_exact&search_string=" +
|
|
|
|
encodeURIComponent(item.name)
|
|
|
|
}
|
|
|
|
isEmbedded={isEmbedded}
|
|
|
|
>
|
|
|
|
Trade Post
|
|
|
|
</LinkBadge>
|
|
|
|
)}
|
|
|
|
</SubtleSkeleton>
|
|
|
|
<SubtleSkeleton isLoaded={searchBadgesAreLoaded}>
|
|
|
|
{!item?.isNc && !item?.isPb && (
|
|
|
|
<LinkBadge
|
|
|
|
href={
|
|
|
|
"http://www.neopets.com/genie.phtml?type=process_genie&criteria=exact&auctiongenie=" +
|
|
|
|
encodeURIComponent(item.name)
|
|
|
|
}
|
|
|
|
isEmbedded={isEmbedded}
|
|
|
|
>
|
|
|
|
Auctions
|
|
|
|
</LinkBadge>
|
|
|
|
)}
|
|
|
|
</SubtleSkeleton>
|
|
|
|
</ItemBadgeList>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-02-22 19:11:03 -08:00
|
|
|
function ItemKindBadgeWithSupportTools({ item }) {
|
2021-02-22 19:37:24 -08:00
|
|
|
const { isSupportUser, supportSecret } = useSupport();
|
|
|
|
const toast = useToast();
|
2021-02-22 19:11:03 -08:00
|
|
|
|
|
|
|
const ncRef = React.useRef(null);
|
|
|
|
|
|
|
|
const isNcAutoDetectedFromRarity =
|
|
|
|
item.rarityIndex === 500 || item.rarityIndex === 0;
|
|
|
|
|
2021-02-22 19:37:24 -08:00
|
|
|
const [mutate, { loading }] = useMutation(gql`
|
|
|
|
mutation ItemPageSupportSetIsManuallyNc(
|
|
|
|
$itemId: ID!
|
|
|
|
$isManuallyNc: Boolean!
|
|
|
|
$supportSecret: String!
|
|
|
|
) {
|
|
|
|
setItemIsManuallyNc(
|
|
|
|
itemId: $itemId
|
|
|
|
isManuallyNc: $isManuallyNc
|
|
|
|
supportSecret: $supportSecret
|
|
|
|
) {
|
|
|
|
id
|
|
|
|
isNc
|
|
|
|
isManuallyNc
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`);
|
|
|
|
|
2021-02-22 19:11:03 -08:00
|
|
|
if (isSupportUser && item.rarityIndex != null && item.isManuallyNc != null) {
|
|
|
|
// TODO: Could code-split this into a SupportOnly file...
|
|
|
|
return (
|
|
|
|
<Popover placement="bottom-start" initialFocusRef={ncRef} showArrow>
|
|
|
|
<PopoverTrigger>
|
|
|
|
<ItemKindBadge isNc={item.isNc} isPb={item.isPb} isEditButton />
|
|
|
|
</PopoverTrigger>
|
|
|
|
<Portal>
|
|
|
|
<PopoverContent padding="4">
|
|
|
|
<PopoverArrow />
|
|
|
|
<VStack spacing="2" align="flex-start">
|
|
|
|
<Flex align="center">
|
|
|
|
<Box as="span" fontWeight="600" marginRight="2">
|
|
|
|
NC:
|
|
|
|
</Box>
|
|
|
|
<Select
|
|
|
|
ref={ncRef}
|
|
|
|
size="xs"
|
|
|
|
value={item.isManuallyNc ? "true" : "false"}
|
2021-02-22 19:37:24 -08:00
|
|
|
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?",
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}}
|
2021-02-22 19:11:03 -08:00
|
|
|
>
|
|
|
|
<option value="false">
|
2021-02-22 19:24:33 -08:00
|
|
|
Auto-detect: {isNcAutoDetectedFromRarity ? "Yes" : "No"}.{" "}
|
|
|
|
(Rarity {item.rarityIndex})
|
|
|
|
</option>
|
|
|
|
<option value="true">Manually set: Yes.</option>
|
|
|
|
</Select>
|
2021-02-22 19:37:24 -08:00
|
|
|
{loading && <Spinner size="sm" marginLeft="2" />}
|
2021-02-22 19:24:33 -08:00
|
|
|
</Flex>
|
|
|
|
<Flex align="center">
|
|
|
|
<Box as="span" fontWeight="600" marginRight="1">
|
|
|
|
PB:
|
|
|
|
</Box>
|
|
|
|
<Select size="xs" isReadOnly value="auto-detect">
|
|
|
|
<option value="auto-detect">
|
|
|
|
Auto-detect: {item.isPb ? "Yes" : "No"}. (from description)
|
|
|
|
</option>
|
|
|
|
<option style={{ fontStyle: "italic" }}>
|
|
|
|
(This cannot be manually set.)
|
2021-02-22 19:11:03 -08:00
|
|
|
</option>
|
|
|
|
</Select>
|
|
|
|
</Flex>
|
2021-02-22 19:24:33 -08:00
|
|
|
<Badge
|
|
|
|
colorScheme="pink"
|
|
|
|
alignSelf="flex-end"
|
|
|
|
marginBottom="-2"
|
|
|
|
marginRight="-2"
|
|
|
|
>
|
|
|
|
Support <span aria-hidden="true">💖</span>
|
|
|
|
</Badge>
|
2021-02-22 19:11:03 -08:00
|
|
|
</VStack>
|
|
|
|
</PopoverContent>
|
|
|
|
</Portal>
|
|
|
|
</Popover>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return <ItemKindBadge isNc={item.isNc} isPb={item.isPb} />;
|
|
|
|
}
|
|
|
|
|
2021-04-07 16:48:41 -07:00
|
|
|
const LinkBadge = React.forwardRef(
|
|
|
|
({ children, href, isEmbedded, ...props }, ref) => {
|
|
|
|
return (
|
|
|
|
<Badge
|
|
|
|
ref={ref}
|
|
|
|
as="a"
|
|
|
|
href={href}
|
|
|
|
display="flex"
|
|
|
|
alignItems="center"
|
|
|
|
// Normally we want to act like a normal webpage, and treat links as
|
|
|
|
// normal. But when we're on the wardrobe page, we want to avoid
|
|
|
|
// disrupting the outfit, and open in a new window instead.
|
|
|
|
target={isEmbedded ? "_blank" : undefined}
|
|
|
|
_focus={{ outline: "none", boxShadow: "outline" }}
|
|
|
|
{...props}
|
|
|
|
>
|
|
|
|
{children}
|
|
|
|
{
|
|
|
|
// We also change the icon to signal whether this will launch in a new
|
|
|
|
// window or not!
|
|
|
|
isEmbedded ? (
|
|
|
|
<ExternalLinkIcon marginLeft="1" />
|
|
|
|
) : (
|
|
|
|
<ChevronRightIcon />
|
|
|
|
)
|
|
|
|
}
|
|
|
|
</Badge>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
);
|
2020-11-23 09:34:46 -08:00
|
|
|
|
|
|
|
const fullDateFormatter = new Intl.DateTimeFormat("en-US", {
|
|
|
|
dateStyle: "long",
|
|
|
|
});
|
|
|
|
const monthYearFormatter = new Intl.DateTimeFormat("en-US", {
|
|
|
|
month: "short",
|
|
|
|
year: "numeric",
|
|
|
|
});
|
|
|
|
const monthDayYearFormatter = new Intl.DateTimeFormat("en-US", {
|
|
|
|
month: "short",
|
|
|
|
day: "numeric",
|
|
|
|
year: "numeric",
|
|
|
|
});
|
|
|
|
function ShortTimestamp({ when }) {
|
|
|
|
const date = new Date(when);
|
|
|
|
|
|
|
|
// To find the start of last month, take today, then set its date to the 1st
|
|
|
|
// and its time to midnight (the start of this month), and subtract one
|
|
|
|
// month. (JS handles negative months and rolls them over correctly.)
|
|
|
|
const startOfLastMonth = new Date();
|
|
|
|
startOfLastMonth.setDate(1);
|
|
|
|
startOfLastMonth.setHours(0);
|
|
|
|
startOfLastMonth.setMinutes(0);
|
|
|
|
startOfLastMonth.setSeconds(0);
|
|
|
|
startOfLastMonth.setMilliseconds(0);
|
|
|
|
startOfLastMonth.setMonth(startOfLastMonth.getMonth() - 1);
|
|
|
|
|
|
|
|
const dateIsOlderThanLastMonth = date < startOfLastMonth;
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Tooltip
|
|
|
|
label={`First seen on ${fullDateFormatter.format(date)}`}
|
|
|
|
placement="top"
|
|
|
|
openDelay={400}
|
|
|
|
>
|
|
|
|
{dateIsOlderThanLastMonth
|
|
|
|
? monthYearFormatter.format(date)
|
|
|
|
: monthDayYearFormatter.format(date)}
|
|
|
|
</Tooltip>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-04-07 16:48:41 -07:00
|
|
|
function WakaPopover({ children, ...props }) {
|
|
|
|
return (
|
2021-04-07 17:37:38 -07:00
|
|
|
<Popover placement="bottom" {...props}>
|
2021-04-07 16:48:41 -07:00
|
|
|
<PopoverTrigger>{children}</PopoverTrigger>
|
|
|
|
<Portal>
|
|
|
|
<PopoverContent>
|
|
|
|
<PopoverArrow />
|
|
|
|
<PopoverBody fontSize="sm">
|
|
|
|
<p>
|
2021-08-07 21:32:22 -07:00
|
|
|
<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 💖
|
2021-04-07 16:48:41 -07:00
|
|
|
</p>
|
|
|
|
<Box height="1em" />
|
|
|
|
<p>
|
2021-08-07 21:32:22 -07:00
|
|
|
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! 💜
|
2021-04-07 16:48:41 -07:00
|
|
|
</p>
|
|
|
|
</PopoverBody>
|
|
|
|
</PopoverContent>
|
|
|
|
</Portal>
|
|
|
|
</Popover>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-08-07 21:32:22 -07:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2020-11-23 09:34:46 -08:00
|
|
|
export default ItemPageLayout;
|