Changeset 7cf8144 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:
9da8027
Parents:
2d2fd8c
git-author:
Bart <bart@…> (08/18/2026 05:28:31 AM)
git-committer:
Bart <bart@…> (08/18/2026 06:47:58 PM)
Message:

Lezen is een weergave van de feed, geen eigen plek

/read was een route met een eigen shell: eigen pagina, eigen chrome-gedrag, en
op een site zonder berichten een 404 -- een dode knop in de switcher. Nu is het
wat het altijd had moeten zijn: een derde waarde van body[data-feed-view], naast
timeline en grid. Dezelfde berichten, andere vorm.

De feed stuurt ze alledrie mee en CSS kiest, precies zoals timeline/grid dat al
deden. Het lijf loopt door PostAccessService, dus een gesloten poort levert ook
hier geen tekst op. "Meer laden" vult de leesstroom OOB mee aan, net als de
tegels.

DE LEGE STAAT zit in de :has(.feed-reader) op de verbergregel. Zonder berichten
rendert de leessectie niet, en dan blijft de tijdlijn staan -- want daar staat
"nog niets geschreven" met de knop om te beginnen. Zonder die voorwaarde was
Lezen op een verse site een wit scherm. Feeds zonder leessectie (cirkel,
archief) houden om dezelfde reden gewoon hun tijdlijn.

Wat weg kon, en dat is de winst:

  • de route, pages/read.ejs, en 'on-read' als page-class;
  • mod/read.js van 222 naar 51 regels. Het ophalen van buren, het corrigeren van de scrollpositie bij invoegen en het onderbreken van het snappen zijn niet opgelost maar OVERBODIG: de feed levert de berichten al. Wat blijft is de tik.
  • read.end en read.load_error: het script plaatst geen zinnen meer.

De balken schuiven niet meer weg. Dat was mooi op een eigen scherm, maar hier
zou het de switcher verbergen waarmee je net koos -- en dan kun je niet terug.

MOD_V naar 4, style.css naar v70.

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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/routes/posts.js

    r2d2fd8c r7cf8144  
    232232
    233233  if (append) {
    234     return renderPage(req, res, 'partials/home-append', { posts, hasMore, nextOffset: offset + FEED_PAGE, moreBase });
     234    return renderPage(req, res, 'partials/home-append', {
     235      posts, hasMore, nextOffset: offset + FEED_PAGE, moreBase,
     236      readerItems: readerItems(site, posts, req),
     237    });
    235238  }
    236239
     
    243246    pinnedPosts,
    244247    posts,
     248    readerItems: readerItems(site, [...pinnedPosts, ...posts], req),
    245249    hasMore, nextOffset: offset + FEED_PAGE, moreBase,
    246250    movedTo,
     
    249253    socialDescr: site.description || site.tagline || '',
    250254    bodyClass: 'on-home',
     255    // mod/read.js: alleen nog de tik-op-een-bericht in de leesweergave.
     256    pageJs: 'read',
    251257  });
    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.
    266 router.get('/read/:slug?', (req, res, next) => {
    267   const site = res.locals.site;
    268   if (!site) return next();
    269 
    270   // Zonder slug: het nieuwste bericht, zodat /read 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-read zet de chrome weg; de mini-topnav blijft (chrome.ejs, _headerless).
    294     bodyClass: 'on-read on-special',
    295     pageJs: 'read',
    296   };
    297   // ALLEEN de leesmodule krijgt één artikel; ?fragment=1 is haar eigen teken.
    298   //
    299   // Hier stond `?partial=1 || hx-request`, en dat waren twee dingen door elkaar.
    300   // Op deze site betekent ?partial=1 al iets anders -- "de pagina zonder de
    301   // schil", voor htmx -- dus een gebooste link naar /read kreeg één kaal
    302   // artikel in #pcms-main geschoven: geen stroom, geen on-read, geen script.
    303   // Onzichtbaar zolang je /read alleen intikt; zichtbaar zodra er een link naar
    304   // toe bestaat (de Lezen-knop in de view-switcher).
    305   //
    306   // Alles wat GEEN fragment vraagt gaat naar pages/read, en renderPage bepaalt
    307   // daar zelf of de schil eromheen moet -- net als bij elke andere pagina.
    308   if (req.query.fragment === '1') {
    309     return renderPage(req, res, 'partials/read-article', model);
    310   }
    311   recordPageview(site.id, req);
    312   return renderPage(req, res, 'pages/read', model);
    313258});
    314259
     
    748693// Renders a post's display HTML: baked content + the dynamic audio/embed layer.
    749694// Extracted so the paid unlock (slice 4) serves the exact same body as the page.
     695// Dezelfde berichten, klaar voor de leesweergave.
     696//
     697// Tijdlijn en Grid tonen kaartjes; Lezen toont het hele stuk. Het is dus geen
     698// andere PAGINA maar een andere vorm van dezelfde rijen -- vandaar dat de feed
     699// ze alledrie meestuurt en CSS kiest, precies zoals timeline/grid dat al deden.
     700//
     701// Het lijf loopt door PostAccessService: een gesloten poort levert hier GEEN
     702// tekst op, want wat niet gerenderd wordt kan ook niet lekken.
     703function readerItems(site, rows, req) {
     704  const viewer = { user: req.session?.user || null, site, unlockedSlug: null };
     705  return rows.map((post) => ({
     706    post,
     707    entry: postEntry(post, viewer, { renderBody: (p) => renderPostBodyHtml(site, p, req) }),
     708  }));
     709}
     710
    750711export function renderPostBodyHtml(site, post, req) {
    751712  let html = (post.content_rendered != null && post.content_rendered !== '')
Note: See TracChangeset for help on using the changeset viewer.