Ignore:
Timestamp:
07/20/2026 07:35:55 AM (7 weeks ago)
Author:
Robin <roboburr@…>
Branches:
main
Children:
520e477
Parents:
7b04d3b
git-author:
Robin <roboburr@…> (07/20/2026 07:18:02 AM)
git-committer:
Robin <roboburr@…> (07/20/2026 07:35:55 AM)
Message:

Feature: Load more on Messages, page 72 (klonkt-demo-r9u, slice 2/4)

Messages now pages in 72s with the shared Load-more button instead of a
hard 80-item cap. getMessages gains an offset arg and pages the merged,
grouped stream by recomputing top-down and slicing [offset, offset+72]
(stable across pages); getNotifications' per-source caps scale with the
requested limit so deep paging can reach older items. The item markup
moved to partials/msg-item.ejs so the page and the append fragment
render identically. The client-side filter/search re-indexes appended
rows on htmx:afterSettle and re-applies the active chip, so search keeps
working across pages. Seen-watermark is only stamped on the first page,
not on appends.

Tests: getMessages offset paging (no overlap, PAGE+1 probe). Browser:
72 -> 144 -> 150 then the button drops; searching a term only present on
page 2 matches after append.

Co-Authored-By: Claude Opus 4.8 <noreply@…>

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/services/ActivityPubService.js

    r7b04d3b r1485933  
    507507// outboxId), sorted as one stream. Consecutive likes/boosts on the same post collapse into
    508508// one grouped item (actors list + count) so activity doesn't drown out conversations.
    509 export function getMessages(slug, limit) {
    510   const items = getNotifications(slug, Math.max(120, (limit || 60)));
     509export function getMessages(slug, limit, offset) {
     510  const off = Math.max(0, offset || 0);
     511  const lim = limit || 60;
     512  // The stream is grouped (consecutive likes/boosts collapse), so paging is done by
     513  // recomputing the whole stream top-down and slicing [off, off+lim] — stable across
     514  // pages. Fetch a buffer past off+lim so grouping-shrinkage can't hide a full page.
     515  const need = off + lim + 100;
     516  const items = getNotifications(slug, need);
    511517  try {
    512     for (const m of listOutbox(slug).slice(0, 80)) {
     518    for (const m of listOutbox(slug).slice(0, need)) {
    513519      items.push({
    514520        type: 'sent', outboxId: m.id, to_handle: m.to_handle, in_reply_to: m.in_reply_to,
     
    530536    out.push(it);
    531537  }
    532   return out.slice(0, limit || 60);
     538  return out.slice(off, off + lim);
    533539}
    534540
     
    26602666// Notifications inbox: new followers + replies/likes/boosts on this site's posts.
    26612667export function getNotifications(slug, limit) {
     2668  // Per-source cap scales with the requested limit so Messages can page deep
     2669  // (Load more). Bounded so a huge offset can't ask for unbounded rows.
     2670  const L = Math.min(1000, Math.max(80, limit || 60));
    26622671  const out = [];
    26632672  try {
    2664     for (const f of db.prepare('SELECT actor_uri, created_at FROM ap_followers WHERE slug = ? ORDER BY created_at DESC LIMIT 50').all(slug)) {
     2673    for (const f of db.prepare('SELECT actor_uri, created_at FROM ap_followers WHERE slug = ? ORDER BY created_at DESC LIMIT ?').all(slug, L)) {
    26652674      out.push({ type: 'follow', handle: deriveHandle(f.actor_uri), url: f.actor_uri, created_at: f.created_at });
    26662675    }
     
    26722681      FROM ap_interactions i LEFT JOIN posts p ON p.id = i.post_id
    26732682      WHERE p.site_id = (SELECT id FROM sites WHERE slug = ?)
    2674       ORDER BY i.created_at DESC LIMIT 80
    2675     `).all(slug);
     2683      ORDER BY i.created_at DESC LIMIT ?
     2684    `).all(slug, L);
    26762685    for (const r of rows) out.push({
    26772686      type: r.kind, name: r.actor_name, handle: r.actor_handle, url: r.actor_url, icon: r.actor_icon,
     
    26822691  } catch { /* ignore */ }
    26832692  try {
    2684     for (const r of db.prepare('SELECT actor_uri, actor_name, actor_handle, actor_icon, content, created_at FROM ap_reports WHERE slug = ? ORDER BY created_at DESC LIMIT 50').all(slug)) {
     2693    for (const r of db.prepare('SELECT actor_uri, actor_name, actor_handle, actor_icon, content, created_at FROM ap_reports WHERE slug = ? ORDER BY created_at DESC LIMIT ?').all(slug, L)) {
    26852694      out.push({ type: 'report', name: r.actor_name, handle: r.actor_handle, url: r.actor_uri, icon: r.actor_icon, content: r.content, created_at: r.created_at });
    26862695    }
    26872696  } catch { /* ignore */ }
    26882697  try {
    2689     for (const r of db.prepare('SELECT object_uri, note_url, actor_uri, actor_name, actor_handle, actor_icon, actor_url, content, created_at FROM ap_mentions WHERE slug = ? ORDER BY created_at DESC LIMIT 50').all(slug)) {
     2698    for (const r of db.prepare('SELECT object_uri, note_url, actor_uri, actor_name, actor_handle, actor_icon, actor_url, content, created_at FROM ap_mentions WHERE slug = ? ORDER BY created_at DESC LIMIT ?').all(slug, L)) {
    26902699      out.push({ type: 'mention', name: r.actor_name, handle: r.actor_handle, url: r.actor_url || r.actor_uri, icon: r.actor_icon, content: stripLeadingMentions(r.content), note_url: r.note_url || r.object_uri, created_at: r.created_at });
    26912700    }
Note: See TracChangeset for help on using the changeset viewer.