Changeset 2bd31d6 in Klonkt


Ignore:
Timestamp:
08/08/2026 01:48:05 AM (5 weeks ago)
Author:
Robin <roboburr@…>
Branches:
main
Children:
50e80ac
Parents:
02c6231
git-author:
Robin <roboburr@…> (08/08/2026 01:47:50 AM)
git-committer:
Robin <roboburr@…> (08/08/2026 01:48:05 AM)
Message:

Zwaaien is een klik, geen paginawissel

Een zwaai is een seintje. Dat de hele pagina ervoor herlaadde -- met een
groene balk bovenaan en je scrollpositie kwijt -- gaf het meer gewicht dan
het heeft.

Nu onderschept mod/messages.js de verzending en stuurt hem met fetch, zoals
like/boost in de Krant al deden. De knoppen maken plaats voor een korte
bevestiging; mislukt het, dan komen ze terug met een schud.

DE VAL HIER, en hij verschilt van like/boost: deze formulieren hebben MEERDERE
verzendknoppen ("Wat leuk!", "Hoe gaat het?", "Terug"), elk met een eigen
name="text". FormData(f) neemt de aangeklikte knop NIET mee -- dat doet de
browser alleen bij een echte verzending. Zonder e.submitter vertrekt er dus
een zwaai zonder tekst. Er is een test die precies dat vastlegt: een verzoek
zonder text moet 400 geven en NIETS versturen.

Het formulier blijft een formulier: zonder JS post het en leidt de route om,
ongewijzigd. De route kiest op X-Requested-With welke van de twee.

Geen aan/uit-knop zoals bij een like: zwaaien valt niet terug te draaien, dus
een toggle zou liegen over wat er kan.

Eigen CSS voor de twee toestanden, want de bestaande klasse nudge bleek
alleen op authorize-interaction opmaak te hebben -- op Berichten deed hij niets
(en doet dat nog steeds niet voor de bookmarklet-knop daar).

5 tests erbij.

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

Files:
1 added
4 edited

Legend:

Unmodified
Added
Removed
  • src/assets/js/mod/messages.js

    r02c6231 r2bd31d6  
    126126  }
    127127
     128  // Zwaaien ter plekke: een seintje is geen paginawissel.
     129  //
     130  // Het formulier blijft een echt formulier -- zonder JS post het en leidt de
     131  // route om, precies als vroeger. Hier onderscheppen we het en sturen het met
     132  // fetch, zoals like/boost in de Krant het al deden.
     133  //
     134  // LET OP DE VERZENDKNOP. Deze formulieren hebben er MEERDERE ("Wat leuk!",
     135  // "Hoe gaat het?", "Terug"), elk met een eigen name="text". FormData(f) neemt
     136  // de aangeklikte knop NIET mee -- dat doet de browser alleen bij een echte
     137  // verzending. Zonder submitter vertrekt er dus een zwaai zonder tekst, en die
     138  // weigert de route. Vandaar submitter, met een terugval op de eerste knop.
     139  document.addEventListener('submit', function (e) {
     140    var f = e.target.closest && e.target.closest('.msg-wave, .msg-quickreply');
     141    if (!f || f.dataset.sending) return;
     142    e.preventDefault();
     143    var knoppen = [].slice.call(f.querySelectorAll('button'));
     144    var knop = e.submitter || knoppen[0];
     145    if (!knop) return;
     146    f.dataset.sending = '1';
     147    knoppen.forEach(function (b) { b.disabled = true; });
     148    var body = new URLSearchParams();
     149    new FormData(f).forEach(function (v, k) { body.append(k, v); });
     150    if (knop.name) body.set(knop.name, knop.value);
     151    fetch(f.action, {
     152      method: 'POST', body: body,
     153      headers: { 'X-Requested-With': 'fetch' },
     154      credentials: 'same-origin',
     155    })
     156      .then(function (r) { return r.ok ? r.json() : null; })
     157      .then(function (j) {
     158        if (!j || !j.ok) throw new Error('geweigerd');
     159        // Gelukt: de knoppen maken plaats voor de bevestiging. Zwaaien is
     160        // eenmalig -- anders dan een like valt het niet terug te draaien, dus
     161        // een aan/uit-knop zou liegen over wat er kan.
     162        var klaar = document.createElement('span');
     163        klaar.className = 'msg-wave-done';
     164        klaar.textContent = '👋 ' + (f.dataset.sent || '');
     165        f.replaceChildren(klaar);
     166      })
     167      .catch(function () {
     168        // Mislukt: alles terug zoals het was, zodat een tweede poging kan.
     169        delete f.dataset.sending;
     170        knoppen.forEach(function (b) { b.disabled = false; });
     171        f.classList.add('is-failed');
     172        setTimeout(function () { f.classList.remove('is-failed'); }, 600);
     173      });
     174  });
     175
    128176  function start() { reindex(); apply(); wireBookmarklet(); }
    129177  start();
  • src/routes/posts.js

    r02c6231 r2bd31d6  
    973973  const to = String(req.body.to || '').trim();
    974974  const text = String(req.body.text || '').trim().slice(0, 200);
    975   if (!site || !/^https?:\/\//i.test(to) || !text) return res.redirect(back + '?error=quickreply');
     975  // Zwaaien is een seintje, en een seintje hoort de pagina niet te herladen.
     976  // De module stuurt hem met X-Requested-With: fetch en krijgt JSON terug;
     977  // zonder JS blijft het formulier gewoon posten en omleiden.
     978  const viaFetch = req.get('X-Requested-With') === 'fetch';
     979  const mis = (reden) => (viaFetch ? res.status(400).json({ ok: false, error: reden }) : res.redirect(back + '?error=' + reden));
     980  if (!site || !/^https?:\/\//i.test(to) || !text) return mis('quickreply');
    976981  try {
    977982    const r = await ActivityPubService.deliverDirectNote(site, { recipients: [to], text, wave: true });
    978     if (r) return res.redirect(back + '?success=wave_sent');
     983    if (r) return viaFetch ? res.json({ ok: true }) : res.redirect(back + '?success=wave_sent');
    979984  } catch { /* fall through */ }
    980   res.redirect(back + '?error=quickreply');
     985  return mis('quickreply');
    981986});
    982987
  • src/views/pages/messages.ejs

    r02c6231 r2bd31d6  
    162162  .msg-wave button { line-height: 1.2; }
    163163
     164  /* Zwaaien gebeurt ter plekke (mod/messages.js). Twee toestanden na de klik:
     165     gelukt, dan vervangt de bevestiging de knoppen -- en mislukt, dan komen de
     166     knoppen terug met een schud, zodat een tweede poging voor de hand ligt.
     167     De bestaande klasse `nudge` kon hier niet voor dienen: die heeft alleen
     168     CSS op authorize-interaction, dus op deze pagina zou hij niets doen. */
     169  .msg-wave-done { font-size: .82rem; color: var(--ink-soft, #888); white-space: nowrap; }
     170  .msg-wave.is-failed, .msg-quickreply.is-failed { animation: msg-wave-shake .4s ease; }
     171  @keyframes msg-wave-shake {
     172    0%, 100% { transform: translateX(0); }
     173    25% { transform: translateX(-4px); }
     174    75% { transform: translateX(4px); }
     175  }
     176  @media (prefers-reduced-motion: reduce) {
     177    .msg-wave.is-failed, .msg-quickreply.is-failed { animation: none; outline: 2px solid var(--accent); }
     178  }
     179
    164180  @media (max-width: 560px) {
    165181    /* Op een telefoon is 40px avatar per bubbel puur verlies: de naam staat er
  • src/views/partials/msg-item.ejs

    r02c6231 r2bd31d6  
    7070                  seintje (FEP-633c), en die twee horen niet op een hoop. %>
    7171              <% if (_rt.actorUri) { %>
    72                 <form method="post" action="<%= (typeof siteUrlBase !== 'undefined' ? siteUrlBase : '') %>/messages/quick-reply" class="msg-wave">
     72                <form method="post" action="<%= (typeof siteUrlBase !== 'undefined' ? siteUrlBase : '') %>/messages/quick-reply" class="msg-wave" data-sent="<%= t('msg.wave_sent') %>">
    7373                  <input type="hidden" name="to" value="<%= _rt.actorUri %>">
    7474                  <button class="btn small" name="text" value="&#128075;" title="<%= t('msg.wave_back') %>">&#128075;</button>
     
    167167            <% } else if ((_t === 'reply' || _t === 'mention' || _t === 'sent') && n.content) { %><div class="msg-content msg-note"><%- include('../partials/note-body', { nb: n }) %></div>
    168168              <% if (_t === 'mention' && n.wave && n.actorUri && canMutate) { %>
    169                 <form class="msg-quickreply" method="post" action="<%= (typeof siteUrlBase !== 'undefined' ? siteUrlBase : '') %>/messages/quick-reply" style="display:flex;gap:6px;flex-wrap:wrap;margin-top:6px">
     169                <form class="msg-quickreply" method="post" action="<%= (typeof siteUrlBase !== 'undefined' ? siteUrlBase : '') %>/messages/quick-reply" data-sent="<%= t('msg.wave_sent') %>" style="display:flex;gap:6px;flex-wrap:wrap;margin-top:6px">
    170170                  <input type="hidden" name="to" value="<%= n.actorUri %>">
    171171                  <button class="btn small" name="text" value="<%= t('msg.wave_r1') %>"><%= t('msg.wave_r1') %></button>
Note: See TracChangeset for help on using the changeset viewer.