Use new page title syntax for list pages

I'm not gonna do SSR here, the pages aren't really designed for partial loading state yet. It could be done, but I'm too sleepy! And it's too much refactor at once.
This commit is contained in:
Emi Matchu 2022-09-15 03:38:42 -07:00
parent 70fc8cdefe
commit e42f39f49b
2 changed files with 181 additions and 170 deletions

View file

@ -31,13 +31,14 @@ import {
WindowScroller, WindowScroller,
} from "react-virtualized"; } from "react-virtualized";
import { Heading1, Heading3, MajorErrorMessage, usePageTitle } from "./util"; import { Heading1, Heading3, MajorErrorMessage } from "./util";
import HangerSpinner from "./components/HangerSpinner"; import HangerSpinner from "./components/HangerSpinner";
import MarkdownAndSafeHTML from "./components/MarkdownAndSafeHTML"; import MarkdownAndSafeHTML from "./components/MarkdownAndSafeHTML";
import ItemCard from "./components/ItemCard"; import ItemCard from "./components/ItemCard";
import useCurrentUser from "./components/useCurrentUser"; import useCurrentUser from "./components/useCurrentUser";
import useSupport from "./WardrobePage/support/useSupport"; import useSupport from "./WardrobePage/support/useSupport";
import WIPCallout from "./components/WIPCallout"; import WIPCallout from "./components/WIPCallout";
import Head from "next/head";
function UserItemListPage() { function UserItemListPage() {
const { query } = useRouter(); const { query } = useRouter();
@ -74,8 +75,6 @@ function UserItemListPage() {
const closetList = data?.closetList; const closetList = data?.closetList;
usePageTitle(closetList?.name);
if (loading) { if (loading) {
return ( return (
<Center> <Center>
@ -111,35 +110,42 @@ function UserItemListPage() {
} }
return ( return (
<Box> <>
<Breadcrumb <Head>
fontSize="sm" {closetList?.name && (
opacity="0.8" <title>{closetList?.name} | Dress to Impress</title>
separator={<ChevronRightIcon marginTop="-2px" />} )}
> </Head>
<BreadcrumbItem> <Box>
<Link href={`/user/${creator.id}/lists`} passHref> <Breadcrumb
<BreadcrumbLink>{creator.username}'s lists</BreadcrumbLink> fontSize="sm"
</Link> opacity="0.8"
</BreadcrumbItem> separator={<ChevronRightIcon marginTop="-2px" />}
{/* TODO: The "wants" version of this link doesn't always scroll down >
* the page properly, now that we're not using the <BreadcrumbItem>
* react-router-hash-link library. Oh well for now! <Link href={`/user/${creator.id}/lists`} passHref>
* Would be nice to fix by having Next.js eliminate the loading <BreadcrumbLink>{creator.username}'s lists</BreadcrumbLink>
* spinner perhaps?...*/} </Link>
<BreadcrumbItem> </BreadcrumbItem>
<Link href={linkBackPath} passHref> {/* TODO: The "wants" version of this link doesn't always scroll down
<BreadcrumbLink>{linkBackText}</BreadcrumbLink> * the page properly, now that we're not using the
</Link> * react-router-hash-link library. Oh well for now!
</BreadcrumbItem> * Would be nice to fix by having Next.js eliminate the loading
</Breadcrumb> * spinner perhaps?...*/}
<Box height="1" /> <BreadcrumbItem>
<ClosetList <Link href={linkBackPath} passHref>
closetList={closetList} <BreadcrumbLink>{linkBackText}</BreadcrumbLink>
isCurrentUser={isCurrentUser} </Link>
headingVariant="top-level" </BreadcrumbItem>
/> </Breadcrumb>
</Box> <Box height="1" />
<ClosetList
closetList={closetList}
isCurrentUser={isCurrentUser}
headingVariant="top-level"
/>
</Box>
</>
); );
} }

View file

@ -33,11 +33,12 @@ import { useRouter } from "next/router";
import { useQuery, useLazyQuery, useMutation } from "@apollo/client"; import { useQuery, useLazyQuery, useMutation } from "@apollo/client";
import HangerSpinner from "./components/HangerSpinner"; import HangerSpinner from "./components/HangerSpinner";
import { Heading1, Heading2, usePageTitle } from "./util"; import { Heading1, Heading2 } from "./util";
import SupportOnly from "./WardrobePage/support/SupportOnly"; import SupportOnly from "./WardrobePage/support/SupportOnly";
import useSupport from "./WardrobePage/support/useSupport"; import useSupport from "./WardrobePage/support/useSupport";
import useCurrentUser from "./components/useCurrentUser"; import useCurrentUser from "./components/useCurrentUser";
import { ClosetList, NeopetsStarIcon } from "./UserItemListPage"; import { ClosetList, NeopetsStarIcon } from "./UserItemListPage";
import Head from "next/head";
const BadgeButton = React.forwardRef((props, ref) => ( const BadgeButton = React.forwardRef((props, ref) => (
<Badge as="button" ref={ref} {...props} /> <Badge as="button" ref={ref} {...props} />
@ -90,7 +91,6 @@ function UserItemListsIndexPage() {
} else { } else {
pageTitleText = null; pageTitleText = null;
} }
usePageTitle(pageTitleText);
if (loading) { if (loading) {
return ( return (
@ -151,118 +151,155 @@ function UserItemListsIndexPage() {
).size; ).size;
return ( return (
<ClassNames> <>
{({ css }) => ( <Head>
<Box> <title>{pageTitleText} | Dress to Impress</title>
<Flex align="center" wrap="wrap-reverse"> </Head>
<Box> <ClassNames>
<Heading1>{pageTitleText}</Heading1> {({ css }) => (
<Wrap spacing="2" opacity="0.7"> <Box>
{data.user.contactNeopetsUsername && ( <Flex align="center" wrap="wrap-reverse">
<WrapItem> <Box>
<Badge <Heading1>{pageTitleText}</Heading1>
as="a" <Wrap spacing="2" opacity="0.7">
href={`http://www.neopets.com/userlookup.phtml?user=${data.user.contactNeopetsUsername}`} {data.user.contactNeopetsUsername && (
display="flex" <WrapItem>
alignItems="center" <Badge
> as="a"
<NeopetsStarIcon marginRight="1" /> href={`http://www.neopets.com/userlookup.phtml?user=${data.user.contactNeopetsUsername}`}
{data.user.contactNeopetsUsername}
</Badge>
</WrapItem>
)}
{data.user.contactNeopetsUsername && (
<WrapItem>
<Badge
as="a"
href={`http://www.neopets.com/neomessages.phtml?type=send&recipient=${data.user.contactNeopetsUsername}`}
display="flex"
alignItems="center"
>
<EmailIcon marginRight="1" />
Neomail
</Badge>
</WrapItem>
)}
<SupportOnly>
<WrapItem>
<UserSupportMenu user={data.user}>
<MenuButton
as={BadgeButton}
display="flex" display="flex"
alignItems="center" alignItems="center"
> >
<EditIcon marginRight="1" /> <NeopetsStarIcon marginRight="1" />
Support {data.user.contactNeopetsUsername}
</MenuButton> </Badge>
</UserSupportMenu> </WrapItem>
</WrapItem> )}
</SupportOnly> {data.user.contactNeopetsUsername && (
{/* Usually I put "Own" before "Want", but this matches the natural <WrapItem>
* order on the page: the _matches_ for things you want are things <Badge
* _this user_ owns, so they come first. I think it's also probably a as="a"
* more natural train of thought: you come to someone's list _wanting_ href={`http://www.neopets.com/neomessages.phtml?type=send&recipient=${data.user.contactNeopetsUsername}`}
* something, and _then_ thinking about what you can offer. */} display="flex"
{!isCurrentUser && numItemsTheyOwnThatYouWant > 0 && ( alignItems="center"
<WrapItem> >
<Badge <EmailIcon marginRight="1" />
as="a" Neomail
href="#owned-items" </Badge>
colorScheme="blue" </WrapItem>
display="flex" )}
alignItems="center" <SupportOnly>
> <WrapItem>
<StarIcon marginRight="1" /> <UserSupportMenu user={data.user}>
{numItemsTheyOwnThatYouWant > 1 <MenuButton
? `${numItemsTheyOwnThatYouWant} items you want` as={BadgeButton}
: "1 item you want"} display="flex"
</Badge> alignItems="center"
</WrapItem> >
)} <EditIcon marginRight="1" />
{!isCurrentUser && numItemsTheyWantThatYouOwn > 0 && ( Support
<WrapItem> </MenuButton>
<Badge </UserSupportMenu>
as="a" </WrapItem>
href="#wanted-items" </SupportOnly>
colorScheme="green" {/* Usually I put "Own" before "Want", but this matches the natural
display="flex" * order on the page: the _matches_ for things you want are things
alignItems="center" * _this user_ owns, so they come first. I think it's also probably a
> * more natural train of thought: you come to someone's list _wanting_
<CheckIcon marginRight="1" /> * something, and _then_ thinking about what you can offer. */}
{numItemsTheyWantThatYouOwn > 1 {!isCurrentUser && numItemsTheyOwnThatYouWant > 0 && (
? `${numItemsTheyWantThatYouOwn} items you own` <WrapItem>
: "1 item you own"} <Badge
</Badge> as="a"
</WrapItem> href="#owned-items"
)} colorScheme="blue"
</Wrap> display="flex"
</Box> alignItems="center"
<Box flex="1 0 auto" width="2" /> >
<Box marginBottom="1"> <StarIcon marginRight="1" />
<UserSearchForm /> {numItemsTheyOwnThatYouWant > 1
</Box> ? `${numItemsTheyOwnThatYouWant} items you want`
</Flex> : "1 item you want"}
</Badge>
</WrapItem>
)}
{!isCurrentUser && numItemsTheyWantThatYouOwn > 0 && (
<WrapItem>
<Badge
as="a"
href="#wanted-items"
colorScheme="green"
display="flex"
alignItems="center"
>
<CheckIcon marginRight="1" />
{numItemsTheyWantThatYouOwn > 1
? `${numItemsTheyWantThatYouOwn} items you own`
: "1 item you own"}
</Badge>
</WrapItem>
)}
</Wrap>
</Box>
<Box flex="1 0 auto" width="2" />
<Box marginBottom="1">
<UserSearchForm />
</Box>
</Flex>
<Box marginTop="4"> <Box marginTop="4">
<Heading2 id="owned-items" marginBottom="2"> <Heading2 id="owned-items" marginBottom="2">
{isCurrentUser
? "Items you own"
: `Items ${data.user.username} owns`}
</Heading2>
<VStack
spacing="8"
alignItems="stretch"
className={css`
clear: both;
`}
>
{listsOfOwnedItems.map((closetList) => (
<ClosetList
key={closetList.id}
closetList={closetList}
isCurrentUser={isCurrentUser}
headingVariant={
closetList.isDefaultList && listsOfOwnedItems.length === 1
? "hidden"
: "list-item"
}
// For default lists, we don't have a separate page, we just
// inline them all here. This is a less-nice experience, but it
// simplifies the single-list page a lot to not have to care,
// and for now we just kinda expect that people who care about
// trade lists enough will group them into lists so it's nbd!
maxNumItemsToShow={!closetList.isDefaultList ? 14 : null}
/>
))}
</VStack>
</Box>
<Box
borderTop="1px solid currentColor"
marginTop="16"
marginBottom="6"
/>
<Heading2 id="wanted-items" marginBottom="2">
{isCurrentUser {isCurrentUser
? "Items you own" ? "Items you want"
: `Items ${data.user.username} owns`} : `Items ${data.user.username} wants`}
</Heading2> </Heading2>
<VStack <VStack spacing="4" alignItems="stretch">
spacing="8" {listsOfWantedItems.map((closetList) => (
alignItems="stretch"
className={css`
clear: both;
`}
>
{listsOfOwnedItems.map((closetList) => (
<ClosetList <ClosetList
key={closetList.id} key={closetList.id}
closetList={closetList} closetList={closetList}
isCurrentUser={isCurrentUser} isCurrentUser={isCurrentUser}
headingVariant={ headingVariant={
closetList.isDefaultList && listsOfOwnedItems.length === 1 closetList.isDefaultList && listsOfWantedItems.length === 1
? "hidden" ? "hidden"
: "list-item" : "list-item"
} }
@ -276,41 +313,9 @@ function UserItemListsIndexPage() {
))} ))}
</VStack> </VStack>
</Box> </Box>
)}
<Box </ClassNames>
borderTop="1px solid currentColor" </>
marginTop="16"
marginBottom="6"
/>
<Heading2 id="wanted-items" marginBottom="2">
{isCurrentUser
? "Items you want"
: `Items ${data.user.username} wants`}
</Heading2>
<VStack spacing="4" alignItems="stretch">
{listsOfWantedItems.map((closetList) => (
<ClosetList
key={closetList.id}
closetList={closetList}
isCurrentUser={isCurrentUser}
headingVariant={
closetList.isDefaultList && listsOfWantedItems.length === 1
? "hidden"
: "list-item"
}
// For default lists, we don't have a separate page, we just
// inline them all here. This is a less-nice experience, but it
// simplifies the single-list page a lot to not have to care,
// and for now we just kinda expect that people who care about
// trade lists enough will group them into lists so it's nbd!
maxNumItemsToShow={!closetList.isDefaultList ? 14 : null}
/>
))}
</VStack>
</Box>
)}
</ClassNames>
); );
} }