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 && (
-
- Super 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 && (
+
+ Super 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;