Changeset 52fc278 in Klonkt


Ignore:
Timestamp:
08/07/2026 01:38:51 PM (5 weeks ago)
Author:
roboburr <roboburr@…>
Branches:
main
Children:
156baa3
Parents:
54ee51c
git-author:
Robin <roboburr@…> (08/07/2026 01:16:27 PM)
git-committer:
roboburr <roboburr@…> (08/07/2026 01:38:51 PM)
Message:

De Krant en de downloadpagina, met het data-kanaal in gebruik (shaer-bqr, stap 3d)

De eerste twee die servergegevens nodig hadden. news gebruikte twee vertalingen,
download een berekende URL.

EEN ONTWERPCORRECTIE die het bouwen opleverde: de gegevens horen gedeclareerd te
worden waar ze BEKEND zijn, niet door de route geregen. fileUrl wordt in de
template zelf berekend, en t() bestaat in een route helemaal niet. Dus geeft de
pagina zijn eigen gegevens mee aan de include:

include('../partials/page-data', { pageData: { fileUrl: fileUrl } })

De route noemt alleen nog de module. Dat scheelt in elke volgende pagina een
omweg langs de route, en het houdt de gegevens naast de plek waar ze vandaan
komen.

Gecontroleerd dat er in GEEN enkele module nog een EJS-tag staat -- dat is de
enige manier om zeker te weten dat er niets stilzwijgend is blijven hangen dat
straks als letterlijke tekst in een statisch bestand belandt.

Templates compileren, suite 565/565.

Location:
src
Files:
2 added
4 edited

Legend:

Unmodified
Added
Removed
  • src/routes/download.js

    r54ee51c r52fc278  
    8585  const fan = req.session && req.session.user;
    8686  renderPage(req, res, 'pages/download', {
     87    pageJs: 'download',
    8788    pageTitle: track.title + ' — download',
    8889    bodyClass: 'on-download',
  • src/routes/posts.js

    r54ee51c r52fc278  
    11941194  }
    11951195  renderPage(req, res, 'pages/news', {
     1196    pageJs: 'news',
    11961197    pageTitle: 'News', bodyClass: 'on-special',
    11971198    timeline, hasMore, nextOffset: offset + FEED_PAGE, moreBase,
  • src/views/pages/download.ejs

    r54ee51c r52fc278  
    2626      <p class="dl-sub"><%= _ready_sub %></p>
    2727      <p><a class="dl-go" href="<%= fileUrl %>"><%= _manual %></a></p>
    28       <script>
    29         // Auto-start de download (zelfde-origin attachment-link).
    30         setTimeout(function(){ try { window.location.href = <%- JSON.stringify(fileUrl) %>; } catch(e){} }, 600);
    31       </script>
     28      <%# Het script staat in assets/js/mod/download.js; de servergegevens gaan via partials/page-data.ejs (shaer-bqr). %>
    3229    <% } else { %>
    3330      <h1 class="dl-h1"><%= _download_btn %> <%= tr.title %></h1>
     
    5754  .dl-go { padding: 12px 20px; border-radius: 10px; border: none; background: var(--accent,#6b8f71); color: #fff; font-weight: 600; font-size: 15px; cursor: pointer; text-decoration: none; display: inline-block; }
    5855</style>
     56<%- include('../partials/page-data', { pageData: { fileUrl: fileUrl } }) %>
  • src/views/pages/news.ejs

    r54ee51c r52fc278  
    4141    .tl-paste-go { background: var(--accent); color: var(--paper); }
    4242  </style>
    43   <script>
    44   (function () {
    45     if (window.__tlPasteWired) return; window.__tlPasteWired = true;
    46     function modal() { return document.getElementById('tl-paste'); }
    47     function openM() {
    48       var m = modal(); if (!m) return;
    49       var inp = m.querySelector('.tl-paste-input');
    50       m.classList.add('is-open');
    51       if (inp) {
    52         inp.value = ''; inp.focus();
    53         if (navigator.clipboard && navigator.clipboard.readText) {
    54           navigator.clipboard.readText().then(function (t) {
    55             t = (t || '').trim();
    56             if (/^https?:\/\//i.test(t) && !inp.value) { inp.value = t; inp.select(); }
    57           }).catch(function () {});
    58         }
    59       }
    60     }
    61     function closeM() { var m = modal(); if (m) m.classList.remove('is-open'); }
    62     function go() {
    63       var m = modal(); if (!m) return;
    64       var inp = m.querySelector('.tl-paste-input'), v = (inp ? inp.value : '').trim();
    65       if (!/^https?:\/\//i.test(v)) { if (inp) inp.focus(); return; }
    66       location.href = '/authorize_interaction?uri=' + encodeURIComponent(v);
    67     }
    68     document.addEventListener('click', function (e) {
    69       if (e.target.closest && e.target.closest('#tl-paste-btn')) { e.preventDefault(); openM(); return; }
    70       if (e.target.closest && e.target.closest('.tl-paste-go')) { e.preventDefault(); go(); return; }
    71       if (e.target.closest && (e.target.closest('.tl-paste-cancel') || e.target.closest('.tl-paste-x'))) { e.preventDefault(); closeM(); return; }
    72       var op = document.querySelector('.tl-paste.is-open'); if (op && e.target === op) closeM();
    73     });
    74     document.addEventListener('keydown', function (e) {
    75       var m = document.querySelector('.tl-paste.is-open'); if (!m) return;
    76       if (e.key === 'Escape') closeM();
    77       else if (e.key === 'Enter' && e.target.closest && e.target.closest('.tl-paste')) { e.preventDefault(); go(); }
    78     });
    79   })();
    80   </script>
     43  <%# Het script staat in assets/js/mod/news.js; de servergegevens gaan via partials/page-data.ejs (shaer-bqr). %>
    8144  <% if (typeof success !== 'undefined' && success) { %><div class="alert alert-success"><%= success %></div><% } %>
    8245  <% if (typeof error !== 'undefined' && error) { %><div class="alert alert-error"><%= error %></div><% } %>
     
    184147  .tl-act-block:hover { background: color-mix(in srgb, #d9534f 14%, transparent); border-color: color-mix(in srgb, #d9534f 50%, transparent); }
    185148</style>
    186 
    187 <script>
    188 (function(){
    189   if (window.__fediRemoteWired) return; window.__fediRemoteWired = true;
    190   var current = null, currentBtn = null;
    191   function close(){ if (current) { current.remove(); current = null; currentBtn = null; } }
    192   function go(raw, uri){ var d=(raw||'').trim().replace(/^@?[^@\s]*@/,'').replace(/^https?:\/\//i,'').replace(/\/.*$/,'').trim(); if(d){ try{ localStorage.setItem('pcmsFediServer', d); }catch(e){} location.href='https://'+d+'/authorize_interaction?uri='+encodeURIComponent(uri||''); } }
    193   function place(f, b){ var r=b.getBoundingClientRect(); f.style.top=(r.bottom+window.scrollY+6)+'px'; f.style.left=Math.max(8, Math.min(r.left+window.scrollX, window.scrollX+window.innerWidth-340))+'px'; }
    194   document.addEventListener('click', function(e){
    195     if (e.target.closest && e.target.closest('.fedi-remote-cancel')) { close(); return; }
    196     if (current && e.target.closest && e.target.closest('.fedi-remote-form')) return;
    197     var b = e.target.closest && e.target.closest('.fedi-remote-reply-btn');
    198     if (b) {
    199       e.preventDefault();
    200       if (currentBtn === b) { close(); return; }
    201       close();
    202       var f = document.createElement('form'); f.className='fedi-remote-form'; f.dataset.uri = b.getAttribute('data-fedi-uri')||'';
    203       f.innerHTML = '<input type="text" autocomplete="off" spellcheck="false"><button type="submit" class="btn btn-primary fedi-remote-go" aria-label="ok">&rarr;</button><button type="button" class="fedi-remote-cancel" aria-label="x">&times;</button>';
    204       var _inp = f.querySelector('input'); _inp.placeholder = b.getAttribute('data-fedi-ph') || 'mastodon.social';
    205       try{ var _sv = localStorage.getItem('pcmsFediServer'); if(_sv) _inp.value = _sv; }catch(e){}
    206       document.body.appendChild(f); place(f, b); current=f; currentBtn=b; _inp.focus(); _inp.select();
    207       return;
    208     }
    209     if (current) close();
    210   });
    211   document.addEventListener('submit', function(e){ var f=e.target.closest && e.target.closest('.fedi-remote-form'); if(!f) return; e.preventDefault(); go(f.querySelector('input').value, f.dataset.uri); });
    212   document.addEventListener('keydown', function(e){ if (e.key === 'Escape') close(); });
    213   window.addEventListener('scroll', close, true);
    214 })();
    215 
    216 /* Short feed videos (gif/cover loops like an animated cover) autoplay + loop muted like a GIF;
    217    longer real videos keep their controls. Decided on the actual duration once metadata loads. */
    218 (function(){
    219   document.querySelectorAll('.tl-media-video').forEach(function(v){
    220     if (v.dataset.gifWired) return; v.dataset.gifWired = '1';
    221     var decide = function(){
    222       if (v.duration && v.duration <= 30) {
    223         v.removeAttribute('controls'); v.loop = true; v.muted = true; v.play().catch(function(){});
    224       }
    225     };
    226     if (v.readyState >= 1) decide(); else v.addEventListener('loadedmetadata', decide, { once: true });
    227   });
    228 })();
    229 
    230 /* Like/Boost toggle in place — POST via fetch, flip the button, stay on the page (no reload, no banner). */
    231 (function(){
    232   if (window.__newsReactWired) return; window.__newsReactWired = true;
    233   document.addEventListener('submit', function(e){
    234     var f = e.target.closest && e.target.closest('.tl-react-form');
    235     if (!f) return;
    236     e.preventDefault();
    237     var btn = f.querySelector('button'); if (!btn || btn.disabled) return;
    238     btn.disabled = true;
    239     var body = new URLSearchParams();
    240     new FormData(f).forEach(function(v, k){ body.append(k, v); });
    241     fetch(f.action, { method: 'POST', body: body, headers: { 'X-Requested-With': 'fetch' }, credentials: 'same-origin' })
    242       .then(function(r){ return r.ok ? r.json() : null; })
    243       .then(function(j){ if (j) btn.classList.toggle('is-on', !!j.on); })
    244       .catch(function(){})
    245       .then(function(){ btn.disabled = false; });
    246   });
    247 })();
    248 </script>
    249 
    250 <script>
    251 // Collapse long post bodies to a max height with a "read more" toggle — only when the content
    252 // actually overflows. Runs on every /news render (full load + htmx swap); a per-element flag
    253 // prevents double-wiring.
    254 (function(){
    255   var RM = '<%= t("tl.read_more") %>', SL = '<%= t("tl.show_less") %>';
    256   document.querySelectorAll('.tl-content:not(.nsfw-media):not([data-rm])').forEach(function(c){
    257     c.setAttribute('data-rm', '1');
    258     if (c.scrollHeight > 360) {
    259       c.classList.add('tl-clamp');
    260       var b = document.createElement('button');
    261       b.type = 'button'; b.className = 'tl-readmore'; b.textContent = RM;
    262       b.addEventListener('click', function(){
    263         b.textContent = c.classList.toggle('tl-clamp') ? RM : SL;
    264       });
    265       c.insertAdjacentElement('afterend', b);
    266     }
    267   });
    268 })();
    269 </script>
     149<%- include('../partials/page-data', { pageData: { readMore: t('tl.read_more'), showLess: t('tl.show_less') } }) %>
Note: See TracChangeset for help on using the changeset viewer.