generate page urls on the pagination (better SEO)
All checks were successful
Deploy / deploy (push) Successful in 6m27s
All checks were successful
Deploy / deploy (push) Successful in 6m27s
This commit is contained in:
parent
ec84a456af
commit
dc7644876b
@ -60,9 +60,21 @@ type Props = {
|
|||||||
* Callback function that is called when the user clicks on a page number.
|
* Callback function that is called when the user clicks on a page number.
|
||||||
*/
|
*/
|
||||||
onPageChange: (page: number) => void;
|
onPageChange: (page: number) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional callback to generate the URL for each page.
|
||||||
|
*/
|
||||||
|
generatePageUrl?: (page: number) => string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Pagination({ mobilePagination, page, totalPages, loadingPage, onPageChange }: Props) {
|
export default function Pagination({
|
||||||
|
mobilePagination,
|
||||||
|
page,
|
||||||
|
totalPages,
|
||||||
|
loadingPage,
|
||||||
|
onPageChange,
|
||||||
|
generatePageUrl,
|
||||||
|
}: Props) {
|
||||||
totalPages = Math.round(totalPages);
|
totalPages = Math.round(totalPages);
|
||||||
const isLoading = loadingPage !== undefined;
|
const isLoading = loadingPage !== undefined;
|
||||||
const [currentPage, setCurrentPage] = useState(page);
|
const [currentPage, setCurrentPage] = useState(page);
|
||||||
@ -72,7 +84,7 @@ export default function Pagination({ mobilePagination, page, totalPages, loading
|
|||||||
}, [page]);
|
}, [page]);
|
||||||
|
|
||||||
const handlePageChange = (newPage: number) => {
|
const handlePageChange = (newPage: number) => {
|
||||||
if (newPage < 1 || newPage > totalPages || newPage == currentPage || isLoading) {
|
if (newPage < 1 || newPage > totalPages || newPage === currentPage || isLoading) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,6 +92,11 @@ export default function Pagination({ mobilePagination, page, totalPages, loading
|
|||||||
onPageChange(newPage);
|
onPageChange(newPage);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleLinkClick = (newPage: number, event: React.MouseEvent) => {
|
||||||
|
event.preventDefault(); // Prevent default navigation behavior
|
||||||
|
handlePageChange(newPage);
|
||||||
|
};
|
||||||
|
|
||||||
const renderPageNumbers = () => {
|
const renderPageNumbers = () => {
|
||||||
const pageNumbers = [];
|
const pageNumbers = [];
|
||||||
const maxPagesToShow = mobilePagination ? 3 : 4;
|
const maxPagesToShow = mobilePagination ? 3 : 4;
|
||||||
@ -95,7 +112,9 @@ export default function Pagination({ mobilePagination, page, totalPages, loading
|
|||||||
pageNumbers.push(
|
pageNumbers.push(
|
||||||
<>
|
<>
|
||||||
<PaginationItemWrapper key="start" isLoadingPage={isLoading}>
|
<PaginationItemWrapper key="start" isLoadingPage={isLoading}>
|
||||||
<PaginationLink onClick={() => handlePageChange(1)}>1</PaginationLink>
|
<PaginationLink href={generatePageUrl ? generatePageUrl(1) : ""} onClick={e => handleLinkClick(1, e)}>
|
||||||
|
1
|
||||||
|
</PaginationLink>
|
||||||
</PaginationItemWrapper>
|
</PaginationItemWrapper>
|
||||||
{/* Only show ellipsis if more than 2 pages from the start */}
|
{/* Only show ellipsis if more than 2 pages from the start */}
|
||||||
{startPage > 2 && (
|
{startPage > 2 && (
|
||||||
@ -111,7 +130,11 @@ export default function Pagination({ mobilePagination, page, totalPages, loading
|
|||||||
for (let i = startPage; i <= endPage; i++) {
|
for (let i = startPage; i <= endPage; i++) {
|
||||||
pageNumbers.push(
|
pageNumbers.push(
|
||||||
<PaginationItemWrapper key={i} isLoadingPage={isLoading}>
|
<PaginationItemWrapper key={i} isLoadingPage={isLoading}>
|
||||||
<PaginationLink isActive={i === currentPage} onClick={() => handlePageChange(i)}>
|
<PaginationLink
|
||||||
|
isActive={i === currentPage}
|
||||||
|
href={generatePageUrl ? generatePageUrl(i) : ""}
|
||||||
|
onClick={e => handleLinkClick(i, e)}
|
||||||
|
>
|
||||||
{loadingPage === i ? <ArrowPathIcon className="w-4 h-4 animate-spin" /> : i}
|
{loadingPage === i ? <ArrowPathIcon className="w-4 h-4 animate-spin" /> : i}
|
||||||
</PaginationLink>
|
</PaginationLink>
|
||||||
</PaginationItemWrapper>
|
</PaginationItemWrapper>
|
||||||
@ -126,7 +149,10 @@ export default function Pagination({ mobilePagination, page, totalPages, loading
|
|||||||
<PaginationContent>
|
<PaginationContent>
|
||||||
{/* Previous button for mobile and desktop */}
|
{/* Previous button for mobile and desktop */}
|
||||||
<PaginationItemWrapper isLoadingPage={isLoading}>
|
<PaginationItemWrapper isLoadingPage={isLoading}>
|
||||||
<PaginationPrevious onClick={() => handlePageChange(currentPage - 1)} />
|
<PaginationPrevious
|
||||||
|
href={generatePageUrl ? generatePageUrl(currentPage - 1) : ""}
|
||||||
|
onClick={e => handleLinkClick(currentPage - 1, e)}
|
||||||
|
/>
|
||||||
</PaginationItemWrapper>
|
</PaginationItemWrapper>
|
||||||
|
|
||||||
{renderPageNumbers()}
|
{renderPageNumbers()}
|
||||||
@ -138,14 +164,22 @@ export default function Pagination({ mobilePagination, page, totalPages, loading
|
|||||||
<PaginationEllipsis className="cursor-default" />
|
<PaginationEllipsis className="cursor-default" />
|
||||||
</PaginationItemWrapper>
|
</PaginationItemWrapper>
|
||||||
<PaginationItemWrapper key="end" isLoadingPage={isLoading}>
|
<PaginationItemWrapper key="end" isLoadingPage={isLoading}>
|
||||||
<PaginationLink onClick={() => handlePageChange(totalPages)}>{totalPages}</PaginationLink>
|
<PaginationLink
|
||||||
|
href={generatePageUrl ? generatePageUrl(totalPages) : ""}
|
||||||
|
onClick={e => handleLinkClick(totalPages, e)}
|
||||||
|
>
|
||||||
|
{totalPages}
|
||||||
|
</PaginationLink>
|
||||||
</PaginationItemWrapper>
|
</PaginationItemWrapper>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Next button for mobile and desktop */}
|
{/* Next button for mobile and desktop */}
|
||||||
<PaginationItemWrapper isLoadingPage={isLoading}>
|
<PaginationItemWrapper isLoadingPage={isLoading}>
|
||||||
<PaginationNext onClick={() => handlePageChange(currentPage + 1)} />
|
<PaginationNext
|
||||||
|
href={generatePageUrl ? generatePageUrl(currentPage + 1) : ""}
|
||||||
|
onClick={e => handleLinkClick(currentPage + 1, e)}
|
||||||
|
/>
|
||||||
</PaginationItemWrapper>
|
</PaginationItemWrapper>
|
||||||
</PaginationContent>
|
</PaginationContent>
|
||||||
</ShadCnPagination>
|
</ShadCnPagination>
|
||||||
|
@ -109,9 +109,6 @@ export default function LeaderboardScores({
|
|||||||
if (leaderboardChanged) {
|
if (leaderboardChanged) {
|
||||||
leaderboardChanged(id);
|
leaderboardChanged(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the URL
|
|
||||||
window.history.replaceState(null, "", `/leaderboard/${id}`);
|
|
||||||
},
|
},
|
||||||
[leaderboardChanged]
|
[leaderboardChanged]
|
||||||
);
|
);
|
||||||
@ -139,6 +136,11 @@ export default function LeaderboardScores({
|
|||||||
}
|
}
|
||||||
}, [currentPage, topOfScoresRef, shouldFetch]);
|
}, [currentPage, topOfScoresRef, shouldFetch]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Update the URL
|
||||||
|
window.history.replaceState(null, "", `/leaderboard/${selectedLeaderboardId}/${currentPage}`);
|
||||||
|
}, [selectedLeaderboardId, currentPage]);
|
||||||
|
|
||||||
if (currentScores === undefined) {
|
if (currentScores === undefined) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -194,6 +196,9 @@ export default function LeaderboardScores({
|
|||||||
page={currentPage}
|
page={currentPage}
|
||||||
totalPages={Math.ceil(currentScores.metadata.total / currentScores.metadata.itemsPerPage)}
|
totalPages={Math.ceil(currentScores.metadata.total / currentScores.metadata.itemsPerPage)}
|
||||||
loadingPage={isLoading ? currentPage : undefined}
|
loadingPage={isLoading ? currentPage : undefined}
|
||||||
|
generatePageUrl={page => {
|
||||||
|
return `/leaderboard/${selectedLeaderboardId}/${page}`;
|
||||||
|
}}
|
||||||
onPageChange={newPage => {
|
onPageChange={newPage => {
|
||||||
setCurrentPage(newPage);
|
setCurrentPage(newPage);
|
||||||
setPreviousPage(currentPage);
|
setPreviousPage(currentPage);
|
||||||
|
@ -123,12 +123,19 @@ export default function PlayerScores({ initialScoreData, initialSearch, player,
|
|||||||
if (scores) handleScoreAnimation();
|
if (scores) handleScoreAnimation();
|
||||||
}, [scores, handleScoreAnimation]);
|
}, [scores, handleScoreAnimation]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the URL to the page.
|
||||||
|
*/
|
||||||
|
const getUrl = (page: number) => {
|
||||||
|
return `/player/${player.id}/${pageState.sort}/${page}${isSearchActive ? `?search=${debouncedSearchTerm}` : ""}`;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle updating the URL when the page number,
|
* Handle updating the URL when the page number,
|
||||||
* sort, or search term changes.
|
* sort, or search term changes.
|
||||||
*/
|
*/
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const newUrl = `/player/${player.id}/${pageState.sort}/${pageState.page}${isSearchActive ? `?search=${debouncedSearchTerm}` : ""}`;
|
const newUrl = getUrl(pageState.page);
|
||||||
window.history.replaceState({ ...window.history.state, as: newUrl, url: newUrl }, "", newUrl);
|
window.history.replaceState({ ...window.history.state, as: newUrl, url: newUrl }, "", newUrl);
|
||||||
}, [pageState, debouncedSearchTerm, player.id, isSearchActive]);
|
}, [pageState, debouncedSearchTerm, player.id, isSearchActive]);
|
||||||
|
|
||||||
@ -215,6 +222,9 @@ export default function PlayerScores({ initialScoreData, initialSearch, player,
|
|||||||
page={pageState.page}
|
page={pageState.page}
|
||||||
totalPages={Math.ceil(currentScores.metadata.total / currentScores.metadata.itemsPerPage)}
|
totalPages={Math.ceil(currentScores.metadata.total / currentScores.metadata.itemsPerPage)}
|
||||||
loadingPage={isLoading ? pageState.page : undefined}
|
loadingPage={isLoading ? pageState.page : undefined}
|
||||||
|
generatePageUrl={page => {
|
||||||
|
return getUrl(page);
|
||||||
|
}}
|
||||||
onPageChange={newPage => {
|
onPageChange={newPage => {
|
||||||
setPreviousPage(pageState.page);
|
setPreviousPage(pageState.page);
|
||||||
setPageState({ ...pageState, page: newPage });
|
setPageState({ ...pageState, page: newPage });
|
||||||
|
Reference in New Issue
Block a user