Changeset 28267519 in Klonkt for src/routes


Ignore:
Timestamp:
07/25/2026 07:43:37 AM (7 weeks ago)
Author:
Robin <roboburr@…>
Branches:
main
Children:
5c373b8
Parents:
05665bc
Message:

Guardian 2: meekijken met je wards (interop-hoofdroute, FEP-633c §5)

Een guardian volgt z'n wards, dus hun posts (ook followers-only) worden bezorgd
in de inbox -> timeline. Volgen is het mechanisme, geen nieuwe fetch, werkt
cross-instance en respecteert zichtbaarheid. Bij eerste contact backfillen we de
recente PUBLIEKE posts als cold start zodat de hoek niet leeg is. Read-only: een
guardian kijkt, publiceert niet.

Klonkt accepteert vandaag elke Follow automatisch, dus een gecommitte guardian
komt er direct in; de expliciete 5.3-gating (auto-accept alleen voor guardians)
is de volgende fase. Authorized-fetch komt als Klonkt-onderlinge verfijning erna.

Changed files:
src/routes/guardian2.js

  • ensureWardConnections(): volg + backfill per ward, alleen eerste keer
  • GET /api/feed: getTimeline gefilterd op je wards, read-only shape

src/views/pages/guardian2.ejs

  • wards-corner sectie bovenaan

src/assets/js/guardian2.js

  • renderFeed/loadFeed; CW ingeklapt; server-gesanitizede HTML

src/assets/css/guardian2.css

  • feed-kaart-styling

src/services/i18n.js

  • guardian2 feed_title/feed_sub (nl/en/de)

remarks: npm test 164/164; feed-api auth-gated. v1 /guardian onaangeraakt.

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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/routes/guardian2.js

    r05665bc r28267519  
    8686  if (!site) return res.status(404).json({ error: 'no_site' });
    8787  res.json(dashboardState(site, resolveLang(req)));
     88});
     89
     90// ── Meekijken (FEP-633c §5, interop-hoofdroute): a committed guardian FOLLOWS
     91//    its wards, so their posts (incl. followers-only) are DELIVERED to the
     92//    guardian's inbox → timeline. The follow is the mechanism; no new fetch.
     93//    First contact also backfills the ward's recent PUBLIC posts as a cold
     94//    start so the corner is not empty before delivery catches up.
     95function ensureWardConnections(site) {
     96  let wards;
     97  try { wards = Guardianship.listWards(site.slug); } catch { return; }
     98  for (const w of wards) {
     99    const already = db.prepare('SELECT 1 FROM ap_following WHERE slug = ? AND actor_uri = ?')
     100      .get(site.slug, w.other_uri);
     101    if (already) continue;
     102    // Follow (guardian's server auto-accepts today; §5.3 gating is a later fase).
     103    AP.followActor(site, w.other_uri).catch(() => { /* retried by the queue */ });
     104    // Cold start: pull recent public posts now so oma sees something at once.
     105    AP.backfillFromOutbox(site.slug, w.other_uri).catch(() => { /* best-effort */ });
     106  }
     107}
     108
     109// ── The wards' corner: your wards' posts, read-only. No reply, no share; a
     110//    guardian watches, it does not publish (Robins besluit).
     111router.get('/api/feed', requireAuth, (req, res) => {
     112  const site = siteForUser(req);
     113  if (!site) return res.status(404).json({ error: 'no_site' });
     114  ensureWardConnections(site);
     115  const wardUris = new Set(Guardianship.listWards(site.slug).map((w) => w.other_uri));
     116  // Only show the wards you actually guard (the timeline can hold more).
     117  const items = AP.getTimeline(site.slug, 60, 0)
     118    .filter((p) => wardUris.has(p.author_uri))
     119    .map((p) => ({
     120      id: p.id,
     121      author: p.author_handle || p.author_name || p.author_uri,
     122      authorName: p.author_name,
     123      authorIcon: p.author_icon,
     124      content: p.content,
     125      url: p.url,
     126      published: p.published || p.created_at,
     127      cw: p.cw || null,
     128      media: p.media_json ? JSON.parse(p.media_json) : [],
     129    }));
     130  res.json({ items, following: wardUris.size });
    88131});
    89132
Note: See TracChangeset for help on using the changeset viewer.