pagination over 100 feed items main
authorxangelo <me@xangelo.ca>
Tue, 13 Feb 2024 16:18:30 +0000 (11:18 -0500)
committerxangelo <me@xangelo.ca>
Tue, 13 Feb 2024 16:18:30 +0000 (11:18 -0500)
src/server.ts
src/types.ts
src/views.ts

index 58e726c4ca8690585ad89e8981555fba235e5ec6..ec9b4d560e60da259582672d2f8f06cae12d1196 100644 (file)
@@ -208,7 +208,8 @@ app.get('/feeds', async (req, res) => {
   const feedList: FeedSchemaWithUnread[] = await db.raw(`
 select 
   f.*,
-  count(fe.feed_id) - sum(fe.is_read) as unread
+  sum(fe.is_read) as read,
+  count(fe.feed_id) as total
 from feeds f 
 join feed_entry fe on fe.feed_id = f.id
 group by fe.feed_id
@@ -223,12 +224,25 @@ group by fe.feed_id
 });
 
 app.get('/feeds/:feed_id', async (req, res) => {
+  const page = parseInt(req.query?.page?.toString() ?? '1') - 1;
+
   const feedEntries = await db.select('*').from('feed_entry').where({
     feed_id: req.params.feed_id
-  }).orderBy('pub_date', 'desc').limit(100);
+  }).orderBy('pub_date', 'desc').limit(100).offset(page * 100);
+
+  const feedData: FeedSchemaWithUnread[] = await db.raw(`
+select 
+  f.*,
+  sum(fe.is_read) as read,
+  count(fe.feed_id) as total
+from feeds f 
+join feed_entry fe on fe.feed_id = f.id
+where f.id = ?
+group by fe.feed_id
+`, [req.params.feed_id]);
 
   if(req.accepts('html')) {
-    res.send(renderReaderAppFeedEntries(feedEntries))
+    res.send(renderReaderAppFeedEntries(page, feedData.pop(), feedEntries))
     return;
   }
   res.json(feedEntries);
index 121076ff00cfdb3161d22b2e8c1765cfee1e0ed5..abb7549745cb1d095265d671de0ed66b4c8f2f7d 100644 (file)
@@ -11,7 +11,8 @@ export type FeedSchema = {
 } & KnexTimestamps;
 
 export type FeedSchemaWithUnread = FeedSchema & {
-  unread: number
+  read: number
+  total: number
 }
 
 export type FeedEntrySchema<T = any> = {
index 2a88a8b618bba8e1464cac943d06a2c280dadb43..486fda1bf56faad11f2add91fa5fdc8758db99e7 100644 (file)
@@ -28,7 +28,7 @@ export function renderFeedItem(entry: FeedWithEntrySchema): string {
 
 export function renderReaderAppFeedListItem(feed: FeedSchemaWithUnread, autoload: boolean = false): string {
   return `<a href="/feeds/${feed.id}" hx-get="feeds/${feed.id}" hx-trigger="click${autoload? ', load': ''}" hx-target=".feed-entries" class="feed" data-id="${feed.id}">
-${feed.title} <span class="unread-count" id="unread-${feed.id}">(${feed.unread})</span>
+${feed.title} <span class="unread-count" id="unread-${feed.id}">(${feed.total - feed.read})</span>
     </a>`;
 
 }
@@ -58,6 +58,29 @@ export function renderReaderAppFeedEntry(entry: FeedWithEntrySchema): string {
 
 }
 
-export function renderReaderAppFeedEntries(list: FeedWithEntrySchema[]): string {
-  return list.map(renderReaderAppFeedEntry).join("\n");
+export function renderPagination(baseLink: string, currentPage: number, totalItems: number, itemsPerPage: number = 100): string {
+  let pageLinks: string[] = ['<p class="align-right">'];
+  const pages = Math.ceil(totalItems/itemsPerPage);
+  for(let i = 1; i <= pages; ++i) {
+    if(i === (currentPage + 1)) {
+      pageLinks.push(i.toString());
+    }
+    else {
+      pageLinks.push(`<a href="#" hx-get="${baseLink}?page=${i}" hx-trigger="click" hx-target=".feed-entries"><button>${i}</button></a>`);
+    }
+  }
+
+  pageLinks.push('</p>');
+  return pageLinks.join("\n");
+}
+
+export function renderReaderAppFeedEntries(page: number, feed: FeedSchemaWithUnread, list: FeedWithEntrySchema[]): string {
+  return `
+  <p>
+    <strong>${feed.title}</strong><br>
+    <code>${feed.url}</code>
+  </p>
+  ${list.map(renderReaderAppFeedEntry).join("\n")}
+  ${renderPagination(`/feeds/${feed.id}`, page, feed.total)}
+  `;
 }