1- import { and , desc , eq , lte , or } from "drizzle-orm" ;
1+ import { and , count , desc , eq , or } from "drizzle-orm" ;
22import { Hono } from "hono" ;
33import xss from "xss" ;
44import { Layout } from "../../components/Layout.tsx" ;
@@ -26,7 +26,7 @@ const profile = new Hono();
2626
2727profile . route ( "/:id{[-a-f0-9]+}" , profilePost ) ;
2828
29- const WINDOW = 30 ;
29+ const PAGE_SIZE = 30 ;
3030
3131profile . get < "/:handle" > ( async ( c ) => {
3232 let handle = c . req . param ( "handle" ) ;
@@ -39,14 +39,38 @@ profile.get<"/:handle">(async (c) => {
3939 const contStr = c . req . query ( "cont" ) ;
4040 const cont = contStr == null || contStr . trim ( ) === "" ? undefined : contStr ;
4141 if ( cont != null && ! isUuid ( cont ) ) return c . notFound ( ) ;
42+ const pageStr = c . req . query ( "page" ) ;
43+ if (
44+ pageStr !== undefined &&
45+ ( Number . isNaN ( Number . parseInt ( pageStr ) ) || Number . parseInt ( pageStr ) < 1 )
46+ ) {
47+ return c . notFound ( ) ;
48+ }
49+ const page =
50+ pageStr !== undefined && ! Number . isNaN ( Number . parseInt ( pageStr ) )
51+ ? Number . parseInt ( pageStr )
52+ : 1 ;
53+ const [ { totalPosts } ] = await db
54+ . select ( { totalPosts : count ( ) } )
55+ . from ( posts )
56+ . where (
57+ and (
58+ eq ( posts . accountId , owner . id ) ,
59+ or ( eq ( posts . visibility , "public" ) , eq ( posts . visibility , "unlisted" ) ) ,
60+ ) ,
61+ ) ;
62+ const maxPage = Math . ceil ( totalPosts / PAGE_SIZE ) ;
63+ if ( page > maxPage ) {
64+ return c . notFound ( ) ;
65+ }
4266 const postList = await db . query . posts . findMany ( {
4367 where : and (
4468 eq ( posts . accountId , owner . id ) ,
4569 or ( eq ( posts . visibility , "public" ) , eq ( posts . visibility , "unlisted" ) ) ,
46- ...( cont == null ? [ ] : [ lte ( posts . id , cont ) ] ) ,
4770 ) ,
4871 orderBy : desc ( posts . id ) ,
49- limit : WINDOW + 1 ,
72+ limit : PAGE_SIZE ,
73+ offset : ( page - 1 ) * PAGE_SIZE ,
5074 with : {
5175 account : true ,
5276 media : true ,
@@ -133,12 +157,13 @@ profile.get<"/:handle">(async (c) => {
133157 const atomUrl = new URL ( c . req . url ) ;
134158 atomUrl . pathname += "/atom.xml" ;
135159 atomUrl . search = "" ;
160+ const newerUrl = page > 1 ? `?page=${ page - 1 } ` : undefined ;
136161 const olderUrl =
137- postList . length > WINDOW ? `?cont =${ postList [ WINDOW ] . id } ` : undefined ;
162+ postList . length === PAGE_SIZE ? `?page =${ page + 1 } ` : undefined ;
138163 return c . html (
139164 < ProfilePage
140165 accountOwner = { owner }
141- posts = { postList . slice ( 0 , WINDOW ) }
166+ posts = { postList . slice ( 0 , PAGE_SIZE ) }
142167 pinnedPosts = { pinnedPostList
143168 . map ( ( p ) => p . post )
144169 . filter (
@@ -147,6 +172,7 @@ profile.get<"/:handle">(async (c) => {
147172 featuredTags = { featuredTagList }
148173 atomUrl = { atomUrl . href }
149174 olderUrl = { olderUrl }
175+ newerUrl = { newerUrl }
150176 /> ,
151177 ) ;
152178} ) ;
@@ -224,6 +250,7 @@ interface ProfilePageProps {
224250 readonly featuredTags : FeaturedTag [ ] ;
225251 readonly atomUrl : string ;
226252 readonly olderUrl ?: string ;
253+ readonly newerUrl ?: string ;
227254}
228255
229256function ProfilePage ( {
@@ -233,6 +260,7 @@ function ProfilePage({
233260 featuredTags,
234261 atomUrl,
235262 olderUrl,
263+ newerUrl,
236264} : ProfilePageProps ) {
237265 return (
238266 < Layout
@@ -267,8 +295,9 @@ function ProfilePage({
267295 { posts . map ( ( post ) => (
268296 < PostView post = { post } />
269297 ) ) }
270- < div style = "text-align: right;" >
271- { olderUrl && < a href = { olderUrl } > Older →</ a > }
298+ < div style = { { display : "flex" , justifyContent : "space-between" } } >
299+ < div > { newerUrl && < a href = { newerUrl } > ← Newer</ a > } </ div >
300+ < div > { olderUrl && < a href = { olderUrl } > Older →</ a > } </ div >
272301 </ div >
273302 </ Layout >
274303 ) ;
0 commit comments