Changeset f5cf299 in Klonkt for src/routes/posts.js


Ignore:
Timestamp:
08/18/2026 06:47:58 PM (3 weeks ago)
Author:
Bart <bart@…>
Branches:
main
Children:
9622f20
Parents:
8b65e67
git-author:
Bart <bart@…> (08/17/2026 07:42:53 PM)
git-committer:
Bart <bart@…> (08/18/2026 06:47:58 PM)
Message:

/lees: één bericht vult het scherm, met zijn buren als schildwachten

Stap 2 van de meeslepende tijdlijn. De route levert één bericht plus de twee
buren uit postNeighbors() -- dezelfde die de gewone postpagina gebruikt, zodat
"vorige" daar en hier hetzelfde betekent, gepinde berichten inbegrepen.

De schildwachten staan BINNEN het artikelblok en niet eromheen. Dat is het punt
waar dit later op zou stukgaan: staan ze erbuiten, dan wijzen ze na een
verwisseling nog naar het vorige bericht. Nu komt de hele staat uit het nieuwe
stuk en hoeft de client niets te onthouden.

?partial=1 geeft alleen het artikel, want dat is precies wat er ingeruild wordt.

Het lijf komt via postEntry(), dus een dichte poort levert hier geen tekst op --
gecontroleerd met een rooktest door de echte server: /lees, /lees/:slug, een
betaald bericht en de partial, en in alle vier staat de tweede alinea nergens in
het antwoord.

'lees' staat in de gereserveerde namen, anders overschaduwt een bericht met die
slug de route. En een noscript-terugval met twee gewone links: een tijdlijn die
alleen met script bestaat, bestaat niet voor wie het uitzet.

Nog niet gebouwd: het overscrollen zelf (stap 3).

Co-Authored-By: Claude Opus 5 <claude@…>

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/routes/posts.js

    r8b65e67 rf5cf299  
    2424import { defaultMinCents as paidDefaultMinCents, patreonUrl as paidPatronUrl } from '../services/PaidPatreonService.js';
    2525import { verifyBlob } from '../services/CryptoBox.js';
     26import { postEntry } from '../services/PostAccessService.js';
    2627import MusicMeta from '../services/MusicMeta.js';
    2728import { mediaDir } from '../config/paths.js';
     
    148149  'authorize_interaction', 'fediverse', 'news', 'following', 'notifications', 'blocking',
    149150  'paid', 'push', 'guardian',
     151  // De meeslepende tijdlijn (Barts idee, naar bartoverkamp.nl). Gereserveerd
     152  // omdat een bericht met deze slug de route anders zou overschaduwen.
     153  'lees',
    150154]);
    151155
     
    246250    bodyClass: 'on-home',
    247251  });
     252});
     253
     254// ==================== LEES (meeslepende tijdlijn) ====================
     255//
     256// Eén bericht vult het scherm, de chrome schuift weg, en je scrollt voorbij de
     257// rand naar het vorige of volgende. Naar bartoverkamp.nl, waar prev/next
     258// schildwachten boven en onder het artikel staan en het overscrollen zelf de
     259// navigatie is.
     260//
     261// De buren komen uit postNeighbors(), dezelfde die de gewone postpagina al
     262// gebruikt -- dus "vorige" en "volgende" betekenen hier precies hetzelfde als
     263// daar, ook voor gepinde berichten.
     264//
     265// ?partial=1 levert alleen het artikel, want dat is wat de schildwacht inruilt.
     266router.get('/lees/:slug?', (req, res, next) => {
     267  const site = res.locals.site;
     268  if (!site) return next();
     269
     270  // Zonder slug: het nieuwste bericht, zodat /lees een ingang is en niet een
     271  // fout. Gepind eerst, net als op de voorpagina.
     272  const post = req.params.slug
     273    ? db.prepare(`SELECT p.*, u.username as author_username FROM posts p JOIN users u ON p.author_id = u.id
     274                  WHERE p.site_id = ? AND p.slug = ?`).get(site.id, req.params.slug)
     275    : db.prepare(`SELECT p.*, u.username as author_username FROM posts p JOIN users u ON p.author_id = u.id
     276                  WHERE p.site_id = ? AND p.status = 'published'
     277                  ORDER BY (p.pinned = 0) ASC, p.pinned ASC, p.published_at DESC LIMIT 1`).get(site.id);
     278  if (!post) return next();
     279
     280  // Het besluit en het lijf komen uit één plek (PostAccessService). Een dichte
     281  // poort levert hier GEEN tekst op: die wordt niet eens gerenderd.
     282  const _u = req.query.u ? verifyBlob(String(req.query.u)) : null;
     283  const unlockedSlug = (_u && _u.purpose === 'unlocked' && _u.siteId === site.id) ? String(_u.post) : null;
     284  const entry = postEntry(post, { user: req.session?.user || null, site, unlockedSlug },
     285    { renderBody: (p) => renderPostBodyHtml(site, p, req) });
     286
     287  if (entry.access === 'forbidden') return next();
     288
     289  const { newerPost, olderPost } = postNeighbors(site, post);
     290  const model = {
     291    post, entry, newerPost, olderPost,
     292    pageTitle: post.title || site.title,
     293    // on-lees zet de chrome weg; de mini-topnav blijft (chrome.ejs, _headerless).
     294    bodyClass: 'on-lees on-special',
     295  };
     296  if (req.query.partial === '1' || req.headers['hx-request'] === 'true') {
     297    return renderPage(req, res, 'partials/lees-article', model);
     298  }
     299  recordPageview(site.id, req);
     300  return renderPage(req, res, 'pages/lees', model);
    248301});
    249302
Note: See TracChangeset for help on using the changeset viewer.