Changeset 7917407 in Klonkt for src


Ignore:
Timestamp:
06/16/2026 02:22:31 AM (3 months ago)
Author:
roboburr <roboburr@…>
Branches:
main
Children:
ff08153
Parents:
b960185
Message:

Circles: local read page /cirkel/:id (stay on your site) + source in new tab

Feed cards now link locally (htmx) to /cirkel/<id> instead of externally. The
read page shows title, 'via <source>', date, cached text (plain, white-space:pre-wrap)
+ cover, with 'Read more at <source>' -> target=_blank.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@…>

Location:
src
Files:
1 added
1 edited

Legend:

Unmodified
Added
Removed
  • src/routes/circle.js

    rb960185 r7917407  
    11/**
    2  * Publieke Cirkel-feed: /cirkel
    3  * Toont de gecachte publieke posts uit je cirkel als statische kaarten.
     2 * Cirkel-feed + lokale lees-pagina.
     3 *   GET /cirkel        -> overzicht (zelfde timeline/grid-view als de home)
     4 *   GET /cirkel/:id    -> losse remote-post in eigen chrome (blijf op je site)
    45 * Alleen actief als tenancy === 'circle' (anders next() -> postsRoutes/404).
    56 * Zie docs/cirkels-v1-spec.md §5c.
     
    1920  try { return s ? JSON.parse(s) : []; } catch { return []; }
    2021}
     22function mediaImage(media_json) {
     23  const media = safeJson(media_json).map((m) => ({ ...m, url: safeUrl(m.url) })).filter((m) => m.url);
     24  return media.find((m) => m.type === 'image') || null;
     25}
    2126
     27// ── Overzicht ────────────────────────────────────────────────
    2228router.get('/cirkel', (req, res, next) => {
    2329  if (getTenancy() !== 'circle') return next();
    2430
    2531  const rows = db.prepare(`
    26     SELECT p.id, p.published, p.title, p.summary, p.url, p.media_json,
    27            a.name AS actor_name, a.url AS actor_url, a.avatar AS actor_avatar
     32    SELECT p.id, p.published, p.title, p.summary, p.media_json,
     33           a.name AS actor_name
    2834    FROM remote_posts p
    2935    JOIN remote_actors a ON a.id = p.actor_id
     
    3339
    3440  const posts = rows.map((r) => {
    35     const media = safeJson(r.media_json)
    36       .map((m) => ({ ...m, url: safeUrl(m.url) }))
    37       .filter((m) => m.url);
    38     const image = media.find((m) => m.type === 'image') || null;
    39     // Vorm de remote-post naar het lokale post-shape zodat post-card/post-tile
    40     // (dezelfde timeline/grid-view als de home) 'm renderen. external_url maakt
    41     // de partials extern-linkend (naar de bron) i.p.v. lokaal/htmx.
     41    const image = mediaImage(r.media_json);
    4242    return {
    4343      id: r.id,
    44       slug: encodeURIComponent(r.id),
     44      // Lokale lees-pagina -> de kaart blijft op de eigen site (post-card linkt
     45      // lokaal + htmx, GEEN external_url).
     46      slug: 'cirkel/' + encodeURIComponent(r.id),
    4547      title: r.title || '(zonder titel)',
    4648      excerpt: r.summary || '',
     
    5254      pinned: 0,
    5355      status: 'published',
    54       external_url: safeUrl(r.url),
    5556      source_name: r.actor_name || 'Onbekend',
    56       source_url: safeUrl(r.actor_url),
    5757    };
    5858  });
     
    6161});
    6262
     63// ── Losse remote-post (lokaal lezen) ─────────────────────────
     64router.get('/cirkel/:id', (req, res, next) => {
     65  if (getTenancy() !== 'circle') return next();
     66
     67  const row = db.prepare(`
     68    SELECT p.id, p.published, p.title, p.summary, p.url, p.media_json,
     69           a.name AS actor_name, a.url AS actor_url, a.avatar AS actor_avatar
     70    FROM remote_posts p
     71    JOIN remote_actors a ON a.id = p.actor_id
     72    WHERE p.id = ?
     73  `).get(req.params.id);
     74
     75  if (!row) return next();
     76
     77  const image = mediaImage(row.media_json);
     78  const post = {
     79    title: row.title || '(zonder titel)',
     80    body: row.summary || '',          // platte tekst (gesanitized bij ingest)
     81    published: row.published,
     82    image: image ? image.url : null,
     83    sourceName: row.actor_name || 'Onbekend',
     84    sourceUrl: safeUrl(row.actor_url),
     85    sourceAvatar: safeUrl(row.actor_avatar),
     86    originalUrl: safeUrl(row.url),
     87  };
     88
     89  renderPage(req, res, 'pages/circle-post', {
     90    pageTitle: post.title,
     91    bodyClass: 'on-cirkel-post',
     92    post,
     93  });
     94});
     95
    6396export default router;
Note: See TracChangeset for help on using the changeset viewer.