Skip to content

Commit

Permalink
🚸 Add prefetching to book/series grid pagination
Browse files Browse the repository at this point in the history
via the shared `Pagination` component
  • Loading branch information
aaronleopold committed Apr 14, 2024
1 parent 1bdc664 commit 2f06b7c
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 67 deletions.
33 changes: 24 additions & 9 deletions packages/browser/src/components/Pagination.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import { cn, cx } from '@stump/components'
import { ArrowLeft, ArrowRight, MoreHorizontal } from 'lucide-react'
import { useMemo } from 'react'
import { useCallback, useMemo } from 'react'
import { useWindowSize } from 'rooks'

import { usePagination } from '../hooks/usePagination'
import PagePopoverForm from './PagePopoverForm'

interface PaginationArrowProps {
type PaginationArrowProps = {
kind: 'previous' | 'next'
isDisabled?: boolean
onClick: () => void
onMouseEnter?: () => void
}

function PaginationArrow({ kind, isDisabled, onClick }: PaginationArrowProps) {
function PaginationArrow({ kind, isDisabled, onClick, onMouseEnter }: PaginationArrowProps) {
const ArrowIcon = kind === 'previous' ? ArrowLeft : ArrowRight

// NOTE: notice I am wrapping the link (which will have pointer-events-none when
Expand All @@ -23,6 +24,7 @@ function PaginationArrow({ kind, isDisabled, onClick }: PaginationArrowProps) {
className={cx('items-center', kind === 'next' ? 'justify-end text-right' : 'justify-start', {
'cursor-not-allowed': isDisabled,
})}
onMouseEnter={!isDisabled ? onMouseEnter : undefined}
>
<button
onClick={onClick}
Expand Down Expand Up @@ -56,16 +58,18 @@ function PaginationArrow({ kind, isDisabled, onClick }: PaginationArrowProps) {
)
}

interface PaginationLinkProps {
type PaginationLinkProps = {
onClick?: () => void
value: number
isActive: boolean
onMouseEnter?: () => void
}

function PaginationLink({ value, onClick, isActive }: PaginationLinkProps) {
function PaginationLink({ value, onClick, isActive, onMouseEnter }: PaginationLinkProps) {
return (
<span
onClick={onClick}
onMouseEnter={onMouseEnter}
className={cn(
'inline-flex cursor-pointer items-center border-t-2 px-4 pt-4 text-xs font-medium text-muted md:text-sm',
{
Expand All @@ -81,18 +85,20 @@ function PaginationLink({ value, onClick, isActive }: PaginationLinkProps) {
)
}

export interface PaginationProps {
export type PaginationProps = {
position?: 'top' | 'bottom'
pages: number
currentPage: number
onChangePage: (page: number) => void
onPrefetchPage?: (page: number) => void
}

export default function Pagination({
position = 'top',
pages,
currentPage,
onChangePage,
onPrefetchPage,
}: PaginationProps) {
const { innerWidth: screenWidth } = useWindowSize()

Expand All @@ -112,9 +118,15 @@ export default function Pagination({

const { pageRange } = usePagination({ currentPage, numbersToShow, totalPages: pages })

function handleEllipsisNavigate(page: number) {
onChangePage(page)
}
const handleEllipsisNavigate = useCallback(
(page: number) => {
onPrefetchPage?.(page)
onChangePage(page)
},
[onChangePage, onPrefetchPage],
)

const handlePrefetchPage = useCallback((page: number) => onPrefetchPage?.(page), [onPrefetchPage])

return (
<nav className="w-full">
Expand All @@ -128,6 +140,7 @@ export default function Pagination({
kind="previous"
onClick={() => onChangePage(currentPage - 1)}
isDisabled={currentPage === 1}
onMouseEnter={() => handlePrefetchPage(currentPage - 1)}
/>

<div className="flex items-center">
Expand All @@ -137,6 +150,7 @@ export default function Pagination({
<PaginationLink
key={`${i}, pagination-${page}`}
onClick={() => onChangePage(page)}
onMouseEnter={() => handlePrefetchPage(page)}
isActive={page === currentPage}
value={page}
/>
Expand Down Expand Up @@ -165,6 +179,7 @@ export default function Pagination({
<PaginationArrow
kind="next"
onClick={() => onChangePage(currentPage + 1)}
onMouseEnter={() => handlePrefetchPage(currentPage + 1)}
isDisabled={currentPage >= pages}
/>
</div>
Expand Down
31 changes: 24 additions & 7 deletions packages/browser/src/components/media/BookSearch.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { usePagedMediaQuery } from '@stump/client'
import { prefetchPagedMedia, usePagedMediaQuery } from '@stump/client'
import { usePreviousIsDifferent } from '@stump/components'
import { Media } from '@stump/types'
import React, { useEffect } from 'react'
import React, { useCallback, useEffect, useMemo } from 'react'

import useIsInView from '@/hooks/useIsInView'

Expand All @@ -23,16 +23,21 @@ type Props = {
*/
export default function BookSearch({ page, page_size, setPage, onBookSelect, showFilters }: Props) {
const { filters } = useFilterContext()

const params = useMemo(
() => ({
page,
page_size,
params: filters,
}),
[page, page_size, filters],
)
const {
isLoading,
isRefetching,
media,
pageData: { current_page, total_pages } = {},
} = usePagedMediaQuery({
page,
page_size,
params: filters,
})
} = usePagedMediaQuery(params)

const differentSearch = usePreviousIsDifferent(filters?.search as string)
useEffect(() => {
Expand All @@ -58,6 +63,16 @@ export default function BookSearch({ page, page_size, setPage, onBookSelect, sho
const hasStuff = total_pages !== undefined && current_page !== undefined && total_pages > 0
const hasFilters = Object.keys(filters || {}).length > 0

const handlePrefetchPage = useCallback(
(page: number) => {
prefetchPagedMedia({
...params,
page,
})
},
[params],
)

return (
<>
<section ref={containerRef} id="grid-top-indicator" className="h-1" />
Expand All @@ -74,6 +89,7 @@ export default function BookSearch({ page, page_size, setPage, onBookSelect, sho
pages={total_pages}
currentPage={current_page}
onChangePage={(page) => setPage(page)}
onPrefetchPage={handlePrefetchPage}
/>
)}
<MediaGrid
Expand All @@ -88,6 +104,7 @@ export default function BookSearch({ page, page_size, setPage, onBookSelect, sho
pages={total_pages}
currentPage={current_page}
onChangePage={(page) => setPage(page)}
onPrefetchPage={handlePrefetchPage}
/>
)}
</div>
Expand Down
52 changes: 37 additions & 15 deletions packages/browser/src/scenes/library/LibraryBooksScene.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { usePagedMediaQuery } from '@stump/client'
import { useEffect } from 'react'
import { prefetchPagedMedia, usePagedMediaQuery } from '@stump/client'
import { useCallback, useEffect, useMemo } from 'react'
import { Helmet } from 'react-helmet'
import { useMediaMatch } from 'rooks'

Expand Down Expand Up @@ -31,28 +31,44 @@ function LibraryBooksScene() {

const { layoutMode } = useLayoutMode()
const { filters } = useFilterContext()

const params = useMemo(
() => ({
page,
page_size: is3XLScreenOrBigger ? 40 : 20,
params: {
...filters,
series: {
library: {
id: library.id,
},
},
},
}),
[page, is3XLScreenOrBigger, filters, library.id],
)
const {
isLoading: isLoadingMedia,
isRefetching: isRefetchingMedia,
media,
pageData,
} = usePagedMediaQuery({
page,
page_size: is3XLScreenOrBigger ? 40 : 20,
params: {
...filters,
series: {
library: {
id: library.id,
},
},
},
})
} = usePagedMediaQuery(params)

const { current_page, total_pages } = pageData || {}

const isOnFirstPage = current_page === 1
const hasStuff = total_pages !== undefined && current_page !== undefined

const handlePrefetchPage = useCallback(
(page: number) => {
prefetchPagedMedia({
...params,
page,
})
},
[params],
)

// TODO: detect if going from page > 1 to page = 1 and scroll to top
useEffect(
() => {
Expand Down Expand Up @@ -106,7 +122,12 @@ function LibraryBooksScene() {

<div className="flex w-full flex-col gap-y-6">
{hasStuff && (
<Pagination pages={total_pages} currentPage={current_page} onChangePage={setPage} />
<Pagination
pages={total_pages}
currentPage={current_page}
onChangePage={setPage}
onPrefetchPage={handlePrefetchPage}
/>
)}
<div className="px-4">{renderContent()}</div>
{hasStuff && (
Expand All @@ -115,6 +136,7 @@ function LibraryBooksScene() {
pages={total_pages}
currentPage={current_page}
onChangePage={setPage}
onPrefetchPage={handlePrefetchPage}
/>
)}
</div>
Expand Down
68 changes: 45 additions & 23 deletions packages/browser/src/scenes/library/LibrarySeriesScene.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { useLibraryByIdQuery, usePagedSeriesQuery, useVisitLibrary } from '@stump/client'
import {
prefetchPagedSeries,
useLibraryByIdQuery,
usePagedSeriesQuery,
useVisitLibrary,
} from '@stump/client'
import { usePreviousIsDifferent } from '@stump/components'
import { useEffect, useRef } from 'react'
import { useCallback, useEffect, useMemo, useRef } from 'react'
import { Helmet } from 'react-helmet'
import { useParams } from 'react-router'
import { useMediaMatch } from 'rooks'
Expand All @@ -12,6 +17,14 @@ import SeriesList from '@/components/series/SeriesList'
import { useLayoutMode, usePageParam } from '@/hooks'
import useIsInView from '@/hooks/useIsInView'

export default function LibrarySeriesSceneWrapper() {
return (
<FilterProvider>
<LibrarySeriesScene />
</FilterProvider>
)
}

function LibrarySeriesScene() {
const is3XLScreenOrBigger = useMediaMatch('(min-width: 1600px)')

Expand All @@ -32,22 +45,27 @@ function LibrarySeriesScene() {
const { isLoading, library } = useLibraryByIdQuery(id)

const { filters } = useFilterContext()

const params = useMemo(
() => ({
page,
page_size: is3XLScreenOrBigger ? 40 : 20,
params: {
...filters,
count_media: true,
library: {
id,
},
},
}),
[page, is3XLScreenOrBigger, filters, id],
)
const {
isLoading: isLoadingSeries,
isRefetching: isRefetchingSeries,
series,
pageData,
} = usePagedSeriesQuery({
page,
page_size: is3XLScreenOrBigger ? 40 : 20,
params: {
...filters,
count_media: true,
library: {
id,
},
},
})
} = usePagedSeriesQuery(params)

const differentSearch = usePreviousIsDifferent(filters?.search as string)
useEffect(() => {
Expand All @@ -62,6 +80,16 @@ function LibrarySeriesScene() {
const hasFilters = Object.keys(filters || {}).length > 0
const hasStuff = total_pages !== undefined && current_page !== undefined

const handlePrefetchPage = useCallback(
(page: number) => {
prefetchPagedSeries({
...params,
page,
})
},
[params],
)

// TODO: detect if going from page > 1 to page = 1 and scroll to top
useEffect(
() => {
Expand Down Expand Up @@ -119,7 +147,8 @@ function LibrarySeriesScene() {
<Pagination
pages={total_pages}
currentPage={current_page}
onChangePage={(page) => setPage(page)}
onChangePage={setPage}
onPrefetchPage={handlePrefetchPage}
/>
)}
<div className="px-4">{renderContent()}</div>
Expand All @@ -128,18 +157,11 @@ function LibrarySeriesScene() {
position="bottom"
pages={total_pages}
currentPage={current_page}
onChangePage={(page) => setPage(page)}
onChangePage={setPage}
onPrefetchPage={handlePrefetchPage}
/>
)}
</div>
</>
)
}

export default function LibrarySeriesSceneWrapper() {
return (
<FilterProvider>
<LibrarySeriesScene />
</FilterProvider>
)
}
Loading

0 comments on commit 2f06b7c

Please sign in to comment.