Skip to content

Commit 5ced0af

Browse files
feat: add a pagination component
1 parent 93e03ce commit 5ced0af

File tree

8 files changed

+143
-23
lines changed

8 files changed

+143
-23
lines changed

pnpm-lock.yaml

+54-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/controllers/get-roms.ts

+24-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { and, eq, inArray, type InferSelectModel } from 'drizzle-orm'
1+
import { and, count, eq, inArray, type InferSelectModel } from 'drizzle-orm'
22
import { compact, keyBy } from 'es-toolkit'
33
import { getContextData } from 'waku/middleware/context'
44
import { romTable } from '../databases/library/schema.ts'
@@ -39,7 +39,16 @@ async function getMetadata(romResults: InferSelectModel<typeof romTable>[]) {
3939
return results
4040
}
4141

42-
export async function getRoms({ id, platform }: { id?: string; platform?: string } = {}) {
42+
type GetRomsReturning = Awaited<ReturnType<typeof getRoms>>
43+
export type Roms = GetRomsReturning['roms']
44+
export type RomsPagination = GetRomsReturning['pagination']
45+
46+
export async function getRoms({
47+
id,
48+
page = 1,
49+
pageSize = 100,
50+
platform,
51+
}: { id?: string; page?: number; pageSize?: number; platform?: string } = {}) {
4352
const { currentUser, db } = getContextData()
4453
const { library } = db
4554

@@ -51,8 +60,19 @@ export async function getRoms({ id, platform }: { id?: string; platform?: string
5160
conditions.push(eq(romTable.platform, platform))
5261
}
5362
const where = and(...conditions)
54-
const romResults = await library.select().from(romTable).orderBy(romTable.file_name).where(where).limit(100)
63+
64+
const offset = (page - 1) * pageSize
65+
const romResults = await library
66+
.select()
67+
.from(romTable)
68+
.orderBy(romTable.file_name)
69+
.where(where)
70+
.offset(offset)
71+
.limit(pageSize)
72+
73+
const [{ total }] = await library.select({ total: count() }).from(romTable).orderBy(romTable.file_name).where(where)
5574

5675
const results = await getMetadata(romResults)
57-
return results
76+
77+
return { pagination: { current: page, pages: Math.ceil(total / pageSize), size: pageSize, total }, roms: results }
5878
}

src/pages/library/atoms.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { atom } from 'jotai'
22
import { defaultPreference, type Preference } from '@/constants/preference.ts'
3-
import type { getRoms } from '@/controllers/get-roms'
3+
import type { Roms } from '@/controllers/get-roms'
44

55
export const serverDataAtom = atom<{ [key: string]: any; preference: Preference }>({
66
preference: defaultPreference,
77
})
88

9-
export const romsAtom = atom<Awaited<ReturnType<typeof getRoms>>>()
9+
export const romsAtom = atom<Roms>()

src/pages/library/components/app-layout.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export default function AppLayout({
4040

4141
<div className='flex h-full flex-1'>
4242
<div className='relative my-4 mr-4 flex flex-1 overflow-hidden rounded bg-zinc-50 shadow-[0_0_12px] shadow-black/10'>
43-
<MainScrollArea className='z-1 relative flex-1 p-4' key={req.url.pathname} size='2'>
43+
<MainScrollArea className='z-1 relative flex-1 p-4' key={req.url.href} size='2'>
4444
<main className='min-h-full'>{children}</main>
4545
</MainScrollArea>
4646
{append}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { Button } from '@radix-ui/themes'
2+
import { range } from 'es-toolkit'
3+
import { Link } from 'waku'
4+
import type { RomsPagination } from '@/controllers/get-roms'
5+
6+
export function GameListPagination({ pagination }: { pagination: RomsPagination }) {
7+
const { current, pages } = pagination
8+
9+
if (pages <= 1) {
10+
return
11+
}
12+
13+
return (
14+
<ul className='flex flex-wrap justify-center gap-2 px-10'>
15+
{range(1, pages).map((page) => (
16+
<li key={page}>
17+
<Button asChild size='3' variant={current === page ? 'solid' : 'soft'}>
18+
{current === page ? (
19+
<span>{page}</span>
20+
) : (
21+
<Link key={page} to={`?page=${page}`}>
22+
{page}
23+
</Link>
24+
)}
25+
</Button>
26+
</li>
27+
))}
28+
</ul>
29+
)
30+
}
+10-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
'use client'
22
import { useAtom } from 'jotai'
33
import { useEffect } from 'react'
4+
import type { Roms, RomsPagination } from '@/controllers/get-roms.ts'
45
import { romsAtom } from '../atoms.ts'
56
import { GameEntry } from './game-entry.tsx'
7+
import { GameListPagination } from './game-list-pagination.tsx' // Import the Pagination component
68

7-
export function GameList({ roms: initialRoms }: { roms: any[] }) {
9+
export function GameList({ pagination, roms: initialRoms }: { pagination: RomsPagination; roms: Roms }) {
810
const [roms, setRoms] = useAtom(romsAtom)
911

1012
const renderedRoms = roms || initialRoms
@@ -14,10 +16,13 @@ export function GameList({ roms: initialRoms }: { roms: any[] }) {
1416
}, [setRoms, initialRoms])
1517

1618
return (
17-
<div className='grid gap-4 [grid-template-columns:repeat(auto-fill,minmax(calc(var(--spacing)*40),1fr))]'>
18-
{renderedRoms.map((rom) => (
19-
<GameEntry key={rom.id} rom={rom} />
20-
))}
19+
<div>
20+
<div className='grid gap-4 [grid-template-columns:repeat(auto-fill,minmax(calc(var(--spacing)*40),1fr))]'>
21+
{renderedRoms.map((rom) => (
22+
<GameEntry key={rom.id} rom={rom} />
23+
))}
24+
</div>
25+
<GameListPagination pagination={pagination} />
2126
</div>
2227
)
2328
}

src/pages/library/page.tsx

+9-4
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,28 @@ import { getRoms } from '@/controllers/get-roms.ts'
22
import AppLayout from './components/app-layout.tsx'
33
import { GameList } from './components/game-list.tsx'
44

5-
export async function LibraryPage() {
6-
const roms = await getRoms()
5+
export async function LibraryPage({ query }: { query: string }) {
6+
const page = Number.parseInt(new URLSearchParams(query).get('page') || '', 10) || 1
7+
const { pagination, roms } = await getRoms({ page })
8+
9+
if (page > 1 && roms.length === 0) {
10+
return '404'
11+
}
712

813
return (
914
<AppLayout>
1015
<title>Library - RetroAssembly</title>
1116
<div className='flex flex-col gap-5'>
1217
<div className='relative flex justify-between px-4 pt-4'>
13-
<h1 className='text-5xl font-[Oswald_Variable] font-semibold'>Library</h1>
18+
<h1 className='text-5xl font-[Oswald_Variable] font-semibold'>Library {page}</h1>
1419
<div className='mt-4 flex items-center gap-2 text-zinc-400'>
1520
<span className='icon-[mdi--bar-chart] text-black' />
1621
<span className='font-[DSEG7_Modern] font-bold text-rose-700'>{roms.length}</span> games for{' '}
1722
<span className='font-[DSEG7_Modern] font-bold text-rose-700'>{8}</span> platforms in total
1823
</div>
1924
</div>
2025
<hr className='border-t-1 border-t-black/20' />
21-
<GameList roms={roms} />
26+
<GameList pagination={pagination} roms={roms} />
2227
</div>
2328
</AppLayout>
2429
)

src/pages/library/platform/page.tsx

+13-3
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,22 @@ import { SidebarLinks } from '../components/sidebar-links.tsx'
77
import { PlatformBackground } from './components/platform-background.tsx'
88
import { UploadButton } from './components/upload-button.tsx'
99

10-
export async function PlatformPage({ platform }) {
10+
interface PlatformPageProps {
11+
platform: string
12+
query: string
13+
}
14+
15+
export async function PlatformPage({ platform, query }: PlatformPageProps) {
1116
if (!platformMap[platform]) {
1217
return '404 not found'
1318
}
1419

15-
const roms = await getRoms({ platform })
20+
const page = Number.parseInt(new URLSearchParams(query).get('page') || '', 10) || 1
21+
const { pagination, roms } = await getRoms({ page, platform })
22+
23+
if (page > 1 && roms.length === 0) {
24+
return '404'
25+
}
1626

1727
return (
1828
<AppLayout append={<PlatformBackground platform={platform} />} sidebar={<SidebarLinks platform={platform} />}>
@@ -21,7 +31,7 @@ export async function PlatformPage({ platform }) {
2131
<div className='flex flex-col gap-5'>
2232
<DeviceInfo platform={platform} />
2333
<hr className='border-t-1 border-t-black/20' />
24-
<GameList roms={roms} />
34+
<GameList pagination={pagination} roms={roms} />
2535
<UploadButton platform={platform} />
2636
</div>
2737
</AppLayout>

0 commit comments

Comments
 (0)