2021-05-21 00:50:55 -07:00
|
|
|
import React from "react";
|
2021-06-21 10:54:31 -07:00
|
|
|
import { Box, Button, Flex, Select } from "@chakra-ui/react";
|
|
|
|
import { Link, useHistory, useLocation } from "react-router-dom";
|
2021-05-21 00:50:55 -07:00
|
|
|
|
Paginate the user outfits page
My main inspiration for doing this is actually our potentially-huge upcoming Vercel bill lol
From inspecting my Honeycomb dashboard, it looks like the main offender for backend CPU time usage is outfit images. And it looks like they come in big spikes, of lots of low usage and then suddenly 1,000 requests in one minute.
My suspicion is that this is from users with many saved outfits loading their outfit page, which previously would show all of them at once.
We do have `loading="lazy"` set, but not all browsers support that yet, and I've had trouble pinning down the exact behavior anyway!
Anyway, paginating makes for a better experience for those huge-list users anyway. We've been meaning to do it, so here we go!
My hope is that this drastically decreases backend CPU hours immediately 🤞 If not, we'll need to investigate in more detail where these outfit image requests are actually coming from!
Note that I added the pagination to the existing `outfits` GraphQL endpoint, rather than creating a new one. I felt comfortable doing this because it requires login anyway, so I'm confident that other clients aren't using it; and because, while this kind of thing often creates a risk of problems with frontend and backend code getting out of sync, I think someone running old frontend code will just see only their first 30 outfits (but no pagination toolbar), and get confused and refresh the page, at which point they'll see all of them. (And I actually _prefer_ that slightly confusing UX, to avoid getting more giant spikes of outfit image requests, lol :p)
2021-11-01 19:33:40 -07:00
|
|
|
function PaginationToolbar({
|
|
|
|
isLoading,
|
|
|
|
totalCount,
|
|
|
|
numPerPage = 30,
|
|
|
|
...props
|
|
|
|
}) {
|
2021-05-21 00:50:55 -07:00
|
|
|
const { search } = useLocation();
|
2021-06-21 10:54:31 -07:00
|
|
|
const history = useHistory();
|
2021-05-21 00:50:55 -07:00
|
|
|
|
|
|
|
const currentOffset =
|
|
|
|
parseInt(new URLSearchParams(search).get("offset")) || 0;
|
|
|
|
|
Paginate the user outfits page
My main inspiration for doing this is actually our potentially-huge upcoming Vercel bill lol
From inspecting my Honeycomb dashboard, it looks like the main offender for backend CPU time usage is outfit images. And it looks like they come in big spikes, of lots of low usage and then suddenly 1,000 requests in one minute.
My suspicion is that this is from users with many saved outfits loading their outfit page, which previously would show all of them at once.
We do have `loading="lazy"` set, but not all browsers support that yet, and I've had trouble pinning down the exact behavior anyway!
Anyway, paginating makes for a better experience for those huge-list users anyway. We've been meaning to do it, so here we go!
My hope is that this drastically decreases backend CPU hours immediately 🤞 If not, we'll need to investigate in more detail where these outfit image requests are actually coming from!
Note that I added the pagination to the existing `outfits` GraphQL endpoint, rather than creating a new one. I felt comfortable doing this because it requires login anyway, so I'm confident that other clients aren't using it; and because, while this kind of thing often creates a risk of problems with frontend and backend code getting out of sync, I think someone running old frontend code will just see only their first 30 outfits (but no pagination toolbar), and get confused and refresh the page, at which point they'll see all of them. (And I actually _prefer_ that slightly confusing UX, to avoid getting more giant spikes of outfit image requests, lol :p)
2021-11-01 19:33:40 -07:00
|
|
|
const currentPageIndex = Math.floor(currentOffset / numPerPage);
|
2021-06-21 10:21:25 -07:00
|
|
|
const currentPageNumber = currentPageIndex + 1;
|
Paginate the user outfits page
My main inspiration for doing this is actually our potentially-huge upcoming Vercel bill lol
From inspecting my Honeycomb dashboard, it looks like the main offender for backend CPU time usage is outfit images. And it looks like they come in big spikes, of lots of low usage and then suddenly 1,000 requests in one minute.
My suspicion is that this is from users with many saved outfits loading their outfit page, which previously would show all of them at once.
We do have `loading="lazy"` set, but not all browsers support that yet, and I've had trouble pinning down the exact behavior anyway!
Anyway, paginating makes for a better experience for those huge-list users anyway. We've been meaning to do it, so here we go!
My hope is that this drastically decreases backend CPU hours immediately 🤞 If not, we'll need to investigate in more detail where these outfit image requests are actually coming from!
Note that I added the pagination to the existing `outfits` GraphQL endpoint, rather than creating a new one. I felt comfortable doing this because it requires login anyway, so I'm confident that other clients aren't using it; and because, while this kind of thing often creates a risk of problems with frontend and backend code getting out of sync, I think someone running old frontend code will just see only their first 30 outfits (but no pagination toolbar), and get confused and refresh the page, at which point they'll see all of them. (And I actually _prefer_ that slightly confusing UX, to avoid getting more giant spikes of outfit image requests, lol :p)
2021-11-01 19:33:40 -07:00
|
|
|
const numTotalPages = totalCount ? Math.ceil(totalCount / numPerPage) : null;
|
2021-06-21 10:21:25 -07:00
|
|
|
|
2021-05-21 00:50:55 -07:00
|
|
|
const prevPageSearchParams = new URLSearchParams(search);
|
Paginate the user outfits page
My main inspiration for doing this is actually our potentially-huge upcoming Vercel bill lol
From inspecting my Honeycomb dashboard, it looks like the main offender for backend CPU time usage is outfit images. And it looks like they come in big spikes, of lots of low usage and then suddenly 1,000 requests in one minute.
My suspicion is that this is from users with many saved outfits loading their outfit page, which previously would show all of them at once.
We do have `loading="lazy"` set, but not all browsers support that yet, and I've had trouble pinning down the exact behavior anyway!
Anyway, paginating makes for a better experience for those huge-list users anyway. We've been meaning to do it, so here we go!
My hope is that this drastically decreases backend CPU hours immediately 🤞 If not, we'll need to investigate in more detail where these outfit image requests are actually coming from!
Note that I added the pagination to the existing `outfits` GraphQL endpoint, rather than creating a new one. I felt comfortable doing this because it requires login anyway, so I'm confident that other clients aren't using it; and because, while this kind of thing often creates a risk of problems with frontend and backend code getting out of sync, I think someone running old frontend code will just see only their first 30 outfits (but no pagination toolbar), and get confused and refresh the page, at which point they'll see all of them. (And I actually _prefer_ that slightly confusing UX, to avoid getting more giant spikes of outfit image requests, lol :p)
2021-11-01 19:33:40 -07:00
|
|
|
const prevPageOffset = currentOffset - numPerPage;
|
2021-05-21 00:50:55 -07:00
|
|
|
prevPageSearchParams.set("offset", prevPageOffset);
|
|
|
|
const prevPageUrl = "?" + prevPageSearchParams.toString();
|
|
|
|
|
|
|
|
const nextPageSearchParams = new URLSearchParams(search);
|
Paginate the user outfits page
My main inspiration for doing this is actually our potentially-huge upcoming Vercel bill lol
From inspecting my Honeycomb dashboard, it looks like the main offender for backend CPU time usage is outfit images. And it looks like they come in big spikes, of lots of low usage and then suddenly 1,000 requests in one minute.
My suspicion is that this is from users with many saved outfits loading their outfit page, which previously would show all of them at once.
We do have `loading="lazy"` set, but not all browsers support that yet, and I've had trouble pinning down the exact behavior anyway!
Anyway, paginating makes for a better experience for those huge-list users anyway. We've been meaning to do it, so here we go!
My hope is that this drastically decreases backend CPU hours immediately 🤞 If not, we'll need to investigate in more detail where these outfit image requests are actually coming from!
Note that I added the pagination to the existing `outfits` GraphQL endpoint, rather than creating a new one. I felt comfortable doing this because it requires login anyway, so I'm confident that other clients aren't using it; and because, while this kind of thing often creates a risk of problems with frontend and backend code getting out of sync, I think someone running old frontend code will just see only their first 30 outfits (but no pagination toolbar), and get confused and refresh the page, at which point they'll see all of them. (And I actually _prefer_ that slightly confusing UX, to avoid getting more giant spikes of outfit image requests, lol :p)
2021-11-01 19:33:40 -07:00
|
|
|
const nextPageOffset = currentOffset + numPerPage;
|
2021-05-21 00:50:55 -07:00
|
|
|
nextPageSearchParams.set("offset", nextPageOffset);
|
|
|
|
const nextPageUrl = "?" + nextPageSearchParams.toString();
|
2021-06-21 13:50:49 -07:00
|
|
|
|
|
|
|
// We disable the buttons if we don't know how many total items there are,
|
|
|
|
// and therefore don't know how far navigation can go. We'll additionally
|
|
|
|
// show a loading spinner if `isLoading` is true. (But it's possible the
|
|
|
|
// buttons might be enabled, even if `isLoading` is true, because maybe
|
|
|
|
// something _else_ is loading. `isLoading` is designed to tell us whether
|
|
|
|
// waiting _might_ give us the data we need!)
|
|
|
|
const prevPageIsDisabled = totalCount == null || prevPageOffset < 0;
|
|
|
|
const nextPageIsDisabled = totalCount == null || nextPageOffset >= totalCount;
|
2021-05-21 00:50:55 -07:00
|
|
|
|
2021-06-21 10:54:31 -07:00
|
|
|
const goToPageNumber = React.useCallback(
|
|
|
|
(newPageNumber) => {
|
|
|
|
const newPageIndex = newPageNumber - 1;
|
Paginate the user outfits page
My main inspiration for doing this is actually our potentially-huge upcoming Vercel bill lol
From inspecting my Honeycomb dashboard, it looks like the main offender for backend CPU time usage is outfit images. And it looks like they come in big spikes, of lots of low usage and then suddenly 1,000 requests in one minute.
My suspicion is that this is from users with many saved outfits loading their outfit page, which previously would show all of them at once.
We do have `loading="lazy"` set, but not all browsers support that yet, and I've had trouble pinning down the exact behavior anyway!
Anyway, paginating makes for a better experience for those huge-list users anyway. We've been meaning to do it, so here we go!
My hope is that this drastically decreases backend CPU hours immediately 🤞 If not, we'll need to investigate in more detail where these outfit image requests are actually coming from!
Note that I added the pagination to the existing `outfits` GraphQL endpoint, rather than creating a new one. I felt comfortable doing this because it requires login anyway, so I'm confident that other clients aren't using it; and because, while this kind of thing often creates a risk of problems with frontend and backend code getting out of sync, I think someone running old frontend code will just see only their first 30 outfits (but no pagination toolbar), and get confused and refresh the page, at which point they'll see all of them. (And I actually _prefer_ that slightly confusing UX, to avoid getting more giant spikes of outfit image requests, lol :p)
2021-11-01 19:33:40 -07:00
|
|
|
const newPageOffset = newPageIndex * numPerPage;
|
2021-06-21 10:54:31 -07:00
|
|
|
|
|
|
|
const newPageSearchParams = new URLSearchParams(search);
|
|
|
|
newPageSearchParams.set("offset", newPageOffset);
|
|
|
|
history.push({ search: newPageSearchParams.toString() });
|
|
|
|
},
|
Paginate the user outfits page
My main inspiration for doing this is actually our potentially-huge upcoming Vercel bill lol
From inspecting my Honeycomb dashboard, it looks like the main offender for backend CPU time usage is outfit images. And it looks like they come in big spikes, of lots of low usage and then suddenly 1,000 requests in one minute.
My suspicion is that this is from users with many saved outfits loading their outfit page, which previously would show all of them at once.
We do have `loading="lazy"` set, but not all browsers support that yet, and I've had trouble pinning down the exact behavior anyway!
Anyway, paginating makes for a better experience for those huge-list users anyway. We've been meaning to do it, so here we go!
My hope is that this drastically decreases backend CPU hours immediately 🤞 If not, we'll need to investigate in more detail where these outfit image requests are actually coming from!
Note that I added the pagination to the existing `outfits` GraphQL endpoint, rather than creating a new one. I felt comfortable doing this because it requires login anyway, so I'm confident that other clients aren't using it; and because, while this kind of thing often creates a risk of problems with frontend and backend code getting out of sync, I think someone running old frontend code will just see only their first 30 outfits (but no pagination toolbar), and get confused and refresh the page, at which point they'll see all of them. (And I actually _prefer_ that slightly confusing UX, to avoid getting more giant spikes of outfit image requests, lol :p)
2021-11-01 19:33:40 -07:00
|
|
|
[search, history, numPerPage]
|
2021-06-21 10:54:31 -07:00
|
|
|
);
|
|
|
|
|
2021-05-21 00:50:55 -07:00
|
|
|
return (
|
2021-06-21 10:21:25 -07:00
|
|
|
<Flex align="center" justify="space-between" {...props}>
|
2021-05-21 00:50:55 -07:00
|
|
|
<Button
|
|
|
|
as={prevPageIsDisabled ? "button" : Link}
|
|
|
|
to={prevPageIsDisabled ? undefined : prevPageUrl}
|
|
|
|
_disabled={{ cursor: isLoading ? "wait" : "not-allowed", opacity: 0.4 }}
|
|
|
|
isDisabled={prevPageIsDisabled}
|
|
|
|
>
|
|
|
|
← Prev
|
|
|
|
</Button>
|
2021-06-21 10:21:25 -07:00
|
|
|
{numTotalPages && (
|
2021-06-21 10:54:31 -07:00
|
|
|
<Flex align="center">
|
|
|
|
<Box flex="0 0 auto">Page</Box>
|
|
|
|
<Box width="1" />
|
|
|
|
<PageNumberSelect
|
|
|
|
currentPageNumber={currentPageNumber}
|
|
|
|
numTotalPages={numTotalPages}
|
|
|
|
onChange={goToPageNumber}
|
|
|
|
marginBottom="-2px"
|
|
|
|
/>
|
|
|
|
<Box width="1" />
|
|
|
|
<Box flex="0 0 auto">of {numTotalPages}</Box>
|
|
|
|
</Flex>
|
2021-06-21 10:21:25 -07:00
|
|
|
)}
|
2021-05-21 00:50:55 -07:00
|
|
|
<Button
|
|
|
|
as={nextPageIsDisabled ? "button" : Link}
|
|
|
|
to={nextPageIsDisabled ? undefined : nextPageUrl}
|
|
|
|
_disabled={{ cursor: isLoading ? "wait" : "not-allowed", opacity: 0.4 }}
|
|
|
|
isDisabled={nextPageIsDisabled}
|
|
|
|
>
|
|
|
|
Next →
|
|
|
|
</Button>
|
|
|
|
</Flex>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-06-21 10:54:31 -07:00
|
|
|
function PageNumberSelect({
|
|
|
|
currentPageNumber,
|
|
|
|
numTotalPages,
|
|
|
|
onChange,
|
|
|
|
...props
|
|
|
|
}) {
|
|
|
|
const allPageNumbers = Array.from({ length: numTotalPages }, (_, i) => i + 1);
|
|
|
|
|
|
|
|
const handleChange = React.useCallback(
|
|
|
|
(e) => onChange(Number(e.target.value)),
|
|
|
|
[onChange]
|
|
|
|
);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Select
|
|
|
|
value={currentPageNumber}
|
|
|
|
onChange={handleChange}
|
|
|
|
width="7ch"
|
|
|
|
variant="flushed"
|
|
|
|
textAlign="center"
|
|
|
|
{...props}
|
|
|
|
>
|
|
|
|
{allPageNumbers.map((pageNumber) => (
|
|
|
|
<option key={pageNumber} value={pageNumber}>
|
|
|
|
{pageNumber}
|
|
|
|
</option>
|
|
|
|
))}
|
|
|
|
</Select>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-05-21 00:50:55 -07:00
|
|
|
export default PaginationToolbar;
|