From 1af034e4e7df69e3b5dc46d9e74d376840da7de8 Mon Sep 17 00:00:00 2001 From: Matchu Date: Mon, 23 Nov 2020 09:34:46 -0800 Subject: [PATCH] refactor ItemPage components to be more reusable this is because I'm about to make trades pages! --- src/app/ItemPage.js | 314 +++++--------------------------------- src/app/ItemPageLayout.js | 254 ++++++++++++++++++++++++++++++ 2 files changed, 295 insertions(+), 273 deletions(-) create mode 100644 src/app/ItemPageLayout.js diff --git a/src/app/ItemPage.js b/src/app/ItemPage.js index 49ab29e..ddb3eb0 100644 --- a/src/app/ItemPage.js +++ b/src/app/ItemPage.js @@ -2,12 +2,10 @@ import React from "react"; import { css } from "emotion"; import { AspectRatio, - Badge, Button, Box, HStack, IconButton, - Skeleton, SkeletonText, Tooltip, VisuallyHidden, @@ -19,7 +17,6 @@ import { } from "@chakra-ui/core"; import { CheckIcon, - ExternalLinkIcon, ChevronRightIcon, StarIcon, WarningIcon, @@ -29,12 +26,8 @@ import gql from "graphql-tag"; import { useQuery, useMutation } from "@apollo/client"; import { useParams } from "react-router-dom"; -import { - ItemBadgeList, - ItemKindBadge, - ItemThumbnail, -} from "./components/ItemCard"; -import { Delay, Heading1, usePageTitle } from "./util"; +import ItemPageLayout, { SubtleSkeleton } from "./ItemPageLayout"; +import { Delay, usePageTitle } from "./util"; import { itemAppearanceFragment, petAppearanceFragment, @@ -57,19 +50,6 @@ function ItemPage() { export function ItemPageContent({ itemId, isEmbedded }) { const { isLoggedIn } = useCurrentUser(); - return ( - - - - {isLoggedIn && } - - - {!isEmbedded && } - - ); -} - -function ItemPageHeader({ itemId, isEmbedded }) { const { error, data } = useQuery( gql` query ItemPage($itemId: ID!) { @@ -89,12 +69,6 @@ function ItemPageHeader({ itemId, isEmbedded }) { usePageTitle(data?.item?.name, { skip: isEmbedded }); - // Show 2 lines of description text placeholder on small screens, or when - // embedded in the wardrobe page's narrow drawer. In larger contexts, show - // just 1 line. - const viewportNumDescriptionLines = useBreakpointValue({ base: 2, md: 1 }); - const numDescriptionLines = isEmbedded ? 2 : viewportNumDescriptionLines; - if (error) { return {error.message}; } @@ -102,213 +76,46 @@ function ItemPageHeader({ itemId, isEmbedded }) { const item = data?.item; return ( - <> - - - - - - - - {item?.name || "Item name here"} - - - + + + + + {isLoggedIn && } + + + {!isEmbedded && } + + + ); +} + +function ItemPageDescription({ description, isEmbedded }) { + // Show 2 lines of description text placeholder on small screens, or when + // embedded in the wardrobe page's narrow drawer. In larger contexts, show + // just 1 line. + const viewportNumDescriptionLines = useBreakpointValue({ base: 2, md: 1 }); + const numDescriptionLines = isEmbedded ? 2 : viewportNumDescriptionLines; + + return ( + + {description || ( + + + + - - - {item?.description || ( - - - - - - )} - - - ); -} - -function ItemPageBadges({ item, isEmbedded }) { - const searchBadgesAreLoaded = item?.name != null && item?.isNc != null; - - return ( - - - - - { - // If the createdAt date is null (loaded and empty), hide the badge. - item.createdAt !== null && ( - - - {item.createdAt && } - - - ) - } - - - Classic DTI - - - - - Jellyneo - - - - {!item?.isNc && !item?.isPb && ( - - Shop Wiz - - )} - - - {!item?.isNc && !item?.isPb && ( - - Trade Post - - )} - - - {!item?.isNc && !item?.isPb && ( - - Auctions - - )} - - - ); -} - -function LinkBadge({ children, href, isEmbedded }) { - return ( - - {children} - { - // We also change the icon to signal whether this will launch in a new - // window or not! - isEmbedded ? : - } - - ); -} - -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 ( - - {dateIsOlderThanLastMonth - ? monthYearFormatter.format(date) - : monthDayYearFormatter.format(date)} - + )} + ); } @@ -860,43 +667,4 @@ function ItemPageOutfitPreview({ itemId }) { ); } -/** - * 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 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! - */ -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 ( - - ); -} - export default ItemPage; diff --git a/src/app/ItemPageLayout.js b/src/app/ItemPageLayout.js new file mode 100644 index 0000000..277c4e6 --- /dev/null +++ b/src/app/ItemPageLayout.js @@ -0,0 +1,254 @@ +import React from "react"; +import { Badge, Box, Skeleton, Tooltip } from "@chakra-ui/core"; +import { ExternalLinkIcon, ChevronRightIcon } from "@chakra-ui/icons"; + +import { + ItemBadgeList, + ItemKindBadge, + ItemThumbnail, +} from "./components/ItemCard"; +import { Heading1 } from "./util"; + +function ItemPageLayout({ children, item, isEmbedded }) { + return ( + + + {children} + + ); +} + +function ItemPageHeader({ item, isEmbedded }) { + return ( + + + + + + + + {item?.name || "Item name here"} + + + + + + ); +} + +/** + * 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 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 ( + + ); +} + +function ItemPageBadges({ item, isEmbedded }) { + const searchBadgesAreLoaded = item?.name != null && item?.isNc != null; + + return ( + + + + + { + // If the createdAt date is null (loaded and empty), hide the badge. + item.createdAt !== null && ( + + + {item.createdAt && } + + + ) + } + + + Classic DTI + + + + + Jellyneo + + + + {!item?.isNc && !item?.isPb && ( + + Shop Wiz + + )} + + + {!item?.isNc && !item?.isPb && ( + + Trade Post + + )} + + + {!item?.isNc && !item?.isPb && ( + + Auctions + + )} + + + ); +} + +function LinkBadge({ children, href, isEmbedded }) { + return ( + + {children} + { + // We also change the icon to signal whether this will launch in a new + // window or not! + isEmbedded ? : + } + + ); +} + +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 ( + + {dateIsOlderThanLastMonth + ? monthYearFormatter.format(date) + : monthDayYearFormatter.format(date)} + + ); +} + +export default ItemPageLayout;