11import * as p from "@clack/prompts" ;
2- import { desc , eq } from "drizzle-orm" ;
2+ import { count , desc , eq } from "drizzle-orm" ;
33import { getD1Database , microPosts } from "./db.ts" ;
44
55type Post = typeof microPosts . $inferSelect ;
66
7+ const PAGE_SIZE = 20 ;
8+
79export async function browse ( ) {
810 p . intro ( "Micro Blog Browser" ) ;
911
@@ -13,21 +15,34 @@ export async function browse() {
1315 try {
1416 const { db, dispose } = await getD1Database ( ) ;
1517
16- // Fetch all posts, newest first
17- const posts = await db . select ( ) . from ( microPosts ) . orderBy ( desc ( microPosts . createdAt ) ) ;
18+ // Get total count
19+ const [ { total } ] = await db . select ( { total : count ( ) } ) . from ( microPosts ) ;
1820
19- spinner . stop ( `Found ${ posts . length } post(s)` ) ;
21+ spinner . stop ( `Found ${ total } post(s)` ) ;
2022
21- if ( posts . length === 0 ) {
23+ if ( total === 0 ) {
2224 p . note ( "No posts yet. Create one with 'micro post'" , "Empty" ) ;
2325 await dispose ( ) ;
2426 return ;
2527 }
2628
29+ // Track pagination state
30+ let currentPage = 0 ;
31+ const totalPages = Math . ceil ( total / PAGE_SIZE ) ;
32+
2733 // Show posts in a loop until user exits
2834 let shouldContinue = true ;
2935
3036 while ( shouldContinue ) {
37+ // Fetch current page of posts
38+ const offset = currentPage * PAGE_SIZE ;
39+ const posts = await db
40+ . select ( )
41+ . from ( microPosts )
42+ . orderBy ( desc ( microPosts . createdAt ) )
43+ . limit ( PAGE_SIZE )
44+ . offset ( offset ) ;
45+
3146 const options = posts . map ( ( post ) => {
3247 const preview =
3348 post . content . length > 60 ? post . content . substring ( 0 , 60 ) + "..." : post . content ;
@@ -39,14 +54,34 @@ export async function browse() {
3954 } ;
4055 } ) ;
4156
57+ // Add pagination controls
58+ const hasPrevious = currentPage > 0 ;
59+ const hasNext = currentPage < totalPages - 1 ;
60+
61+ if ( hasPrevious ) {
62+ options . push ( {
63+ value : - 2 ,
64+ label : "← Previous page" ,
65+ hint : `Page ${ currentPage } /${ totalPages } ` ,
66+ } ) ;
67+ }
68+
69+ if ( hasNext ) {
70+ options . push ( {
71+ value : - 3 ,
72+ label : "Next page →" ,
73+ hint : `Page ${ currentPage + 2 } /${ totalPages } ` ,
74+ } ) ;
75+ }
76+
4277 options . push ( {
4378 value : - 1 ,
4479 label : "Exit" ,
4580 hint : "" ,
4681 } ) ;
4782
4883 const selectedId = await p . select ( {
49- message : " Select a post to view options" ,
84+ message : ` Select a post to view options (Page ${ currentPage + 1 } / ${ totalPages } )` ,
5085 options,
5186 } ) ;
5287
@@ -55,11 +90,34 @@ export async function browse() {
5590 continue ;
5691 }
5792
93+ // Handle pagination
94+ if ( selectedId === - 2 ) {
95+ currentPage = Math . max ( 0 , currentPage - 1 ) ;
96+ continue ;
97+ }
98+
99+ if ( selectedId === - 3 ) {
100+ currentPage = Math . min ( totalPages - 1 , currentPage + 1 ) ;
101+ continue ;
102+ }
103+
58104 const selectedPost = posts . find ( ( p ) => p . id === selectedId ) ;
59105 if ( ! selectedPost ) continue ;
60106
61107 // Show post details and actions
62- await showPostActions ( db , selectedPost , posts ) ;
108+ const deleted = await showPostActions ( db , selectedPost ) ;
109+
110+ // If post was deleted, refresh the page
111+ if ( deleted ) {
112+ // Reload total count
113+ const [ { total : newTotal } ] = await db . select ( { total : count ( ) } ) . from ( microPosts ) ;
114+ const newTotalPages = Math . ceil ( newTotal / PAGE_SIZE ) ;
115+
116+ // If we deleted the last post on a page, go back one page
117+ if ( currentPage >= newTotalPages && currentPage > 0 ) {
118+ currentPage = newTotalPages - 1 ;
119+ }
120+ }
63121 }
64122
65123 await dispose ( ) ;
@@ -73,8 +131,7 @@ export async function browse() {
73131async function showPostActions (
74132 db : ReturnType < typeof getD1Database > extends Promise < { db : infer D } > ? D : never ,
75133 post : Post ,
76- allPosts : Post [ ] ,
77- ) {
134+ ) : Promise < boolean > {
78135 const syndicatedData =
79136 ( post . syndicatedTo as Array < { platform : string ; id : string ; url : string } > | null ) || [ ] ;
80137 const syndicatedPlatforms = syndicatedData . map ( ( s ) => s . platform ) ;
@@ -105,7 +162,7 @@ async function showPostActions(
105162 } ) ;
106163
107164 if ( p . isCancel ( action ) || action === "back" ) {
108- return ;
165+ return false ;
109166 }
110167
111168 switch ( action ) {
@@ -114,27 +171,27 @@ async function showPostActions(
114171 const url = `https://just-be.dev/micro#post-${ post . id } ` ;
115172 console . log ( `Opening: ${ url } ` ) ;
116173 await Bun . spawn ( [ "open" , url ] ) ;
117- break ;
174+ return false ;
118175 }
119176 case "open-bluesky" : {
120177 const blueskyData = syndicatedData . find ( ( s ) => s . platform === "bluesky" ) ;
121178 if ( ! blueskyData ) {
122179 p . note ( "This post hasn't been syndicated to Bluesky yet" , "Not available" ) ;
123- break ;
180+ return false ;
124181 }
125182 console . log ( `Opening: ${ blueskyData . url } ` ) ;
126183 await Bun . spawn ( [ "open" , blueskyData . url ] ) ;
127- break ;
184+ return false ;
128185 }
129186 case "open-twitter" : {
130187 const twitterData = syndicatedData . find ( ( s ) => s . platform === "twitter" ) ;
131188 if ( ! twitterData ) {
132189 p . note ( "This post hasn't been syndicated to Twitter yet" , "Not available" ) ;
133- break ;
190+ return false ;
134191 }
135192 console . log ( `Opening: ${ twitterData . url } ` ) ;
136193 await Bun . spawn ( [ "open" , twitterData . url ] ) ;
137- break ;
194+ return false ;
138195 }
139196 case "delete" : {
140197 const confirmed = await p . confirm ( {
@@ -144,22 +201,18 @@ async function showPostActions(
144201
145202 if ( p . isCancel ( confirmed ) || ! confirmed ) {
146203 p . note ( "Delete cancelled" , "Cancelled" ) ;
147- break ;
204+ return false ;
148205 }
149206
150207 const spinner = p . spinner ( ) ;
151208 spinner . start ( "Deleting post..." ) ;
152209
153210 await db . delete ( microPosts ) . where ( eq ( microPosts . id , post . id ) ) ;
154211
155- // Remove from the posts array
156- const index = allPosts . findIndex ( ( p ) => p . id === post . id ) ;
157- if ( index > - 1 ) {
158- allPosts . splice ( index , 1 ) ;
159- }
160-
161212 spinner . stop ( `Post #${ post . id } deleted successfully` ) ;
162- break ;
213+ return true ;
163214 }
164215 }
216+
217+ return false ;
165218}
0 commit comments