Changeset 2bd31d6 in Klonkt
- Timestamp:
- 08/08/2026 01:48:05 AM (5 weeks ago)
- 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)
- Files:
-
- 1 added
- 4 edited
-
src/assets/js/mod/messages.js (modified) (1 diff)
-
src/routes/posts.js (modified) (1 diff)
-
src/views/pages/messages.ejs (modified) (1 diff)
-
src/views/partials/msg-item.ejs (modified) (2 diffs)
-
test/messages-wave.test.js (added)
Legend:
- Unmodified
- Added
- Removed
-
src/assets/js/mod/messages.js
r02c6231 r2bd31d6 126 126 } 127 127 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 128 176 function start() { reindex(); apply(); wireBookmarklet(); } 129 177 start(); -
src/routes/posts.js
r02c6231 r2bd31d6 973 973 const to = String(req.body.to || '').trim(); 974 974 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'); 976 981 try { 977 982 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'); 979 984 } catch { /* fall through */ } 980 re s.redirect(back + '?error=quickreply');985 return mis('quickreply'); 981 986 }); 982 987 -
src/views/pages/messages.ejs
r02c6231 r2bd31d6 162 162 .msg-wave button { line-height: 1.2; } 163 163 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 164 180 @media (max-width: 560px) { 165 181 /* Op een telefoon is 40px avatar per bubbel puur verlies: de naam staat er -
src/views/partials/msg-item.ejs
r02c6231 r2bd31d6 70 70 seintje (FEP-633c), en die twee horen niet op een hoop. %> 71 71 <% 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') %>"> 73 73 <input type="hidden" name="to" value="<%= _rt.actorUri %>"> 74 74 <button class="btn small" name="text" value="👋" title="<%= t('msg.wave_back') %>">👋</button> … … 167 167 <% } else if ((_t === 'reply' || _t === 'mention' || _t === 'sent') && n.content) { %><div class="msg-content msg-note"><%- include('../partials/note-body', { nb: n }) %></div> 168 168 <% 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"> 170 170 <input type="hidden" name="to" value="<%= n.actorUri %>"> 171 171 <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.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)