Changeset a5a6249 in Klonkt for src/assets/js
- Timestamp:
- 08/12/2026 07:52:13 PM (3 months ago)
- Children:
- 084b046
- Parents:
- 303bfc4
- File:
-
- 1 edited
-
src/assets/js/ward.js (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/assets/js/ward.js
r303bfc4 ra5a6249 98 98 } 99 99 100 /** 101 * De kaarten. De body komt AF van de server (dezelfde partial als de Krant), 102 * dus hier alleen invoegen; zelf HTML samenstellen zou opmaak, media en 103 * quote-kaarten opnieuw moeten uitvinden en er anders uit laten zien. 104 */ 105 function renderFeed(items) { 106 const box = document.getElementById('card-list'); 107 box.replaceChildren(); 108 document.getElementById('feed-empty').hidden = items.length > 0; 109 110 items.forEach((it) => { 111 const card = el('article', 'post'); 112 card.dataset.note = it.id; // waar de boei op mikt 113 const head = el('div', 'row'); 114 if (it.authorIcon) { 115 const img = el('img', 'avatar'); 116 img.src = it.authorIcon; img.alt = ''; img.loading = 'lazy'; 117 head.append(img); 118 } 119 head.append(el('span', 'name', it.authorName || it.author)); 120 head.append(el('span', 'when', it.whenText || '')); 121 card.append(head); 122 123 if (it.cw) card.append(el('p', 'cw', it.cw)); 124 const body = el('div', 'body'); 125 body.innerHTML = it.bodyHtml || ''; // server-gerenderd en gesaniteerd 126 card.append(body); 127 box.append(card); 128 }); 129 } 130 131 async function loadFeed() { 132 try { 133 const r = await fetch('/ward/api/feed', { headers: { Accept: 'application/json' } }); 134 if (!r.ok) return; 135 renderFeed((await r.json()).items || []); 136 } catch { /* de pagina blijft staan met wat er al is */ } 137 } 138 139 /** 140 * De reddingsboei: sleep hem op een kaart en je guardians krijgen die post. 141 * 142 * Pointer-events in plaats van muis- of touch-events apart, want dat is het 143 * enige dat op een telefoon en met een muis hetzelfde doet. `setPointerCapture` 144 * zorgt dat de sleep van jou blijft, en `touch-action: none` op de boei houdt 145 * de pagina stil terwijl je hem verplaatst: zonder dat vecht het slepen met 146 * scrollen en verlies je hem halverwege. 147 */ 148 function wireBuoy() { 149 const buoy = document.getElementById('buoy'); 150 const dlg = document.getElementById('help-dialog'); 151 let dragging = false, dx = 0, dy = 0, target = null; 152 153 const cardUnder = (x, y) => document.elementFromPoint(x, y)?.closest('.post'); 154 155 buoy.addEventListener('pointerdown', (e) => { 156 dragging = true; 157 buoy.setPointerCapture(e.pointerId); 158 const r = buoy.getBoundingClientRect(); 159 dx = e.clientX - r.left; dy = e.clientY - r.top; 160 buoy.classList.add('dragging'); 161 }); 162 163 buoy.addEventListener('pointermove', (e) => { 164 if (!dragging) return; 165 buoy.style.left = `${e.clientX - dx}px`; 166 buoy.style.top = `${e.clientY - dy}px`; 167 buoy.style.right = 'auto'; buoy.style.bottom = 'auto'; 168 // Onderweg oplichten waar hij zou landen, zodat je ziet wat je kiest. 169 document.querySelectorAll('.post.hover').forEach((n) => n.classList.remove('hover')); 170 cardUnder(e.clientX, e.clientY)?.classList.add('hover'); 171 }); 172 173 const drop = (e) => { 174 if (!dragging) return; 175 dragging = false; 176 buoy.classList.remove('dragging'); 177 const card = cardUnder(e.clientX, e.clientY); 178 document.querySelectorAll('.post.hover').forEach((n) => n.classList.remove('hover')); 179 // Terug naar zijn hoek: de boei hoort altijd op dezelfde plek te liggen. 180 buoy.style.left = buoy.style.top = 'auto'; 181 buoy.style.right = ''; buoy.style.bottom = ''; 182 if (card) askHelp(card.dataset.note, card.querySelector('.name')?.textContent || ''); 183 }; 184 buoy.addEventListener('pointerup', drop); 185 buoy.addEventListener('pointercancel', drop); 186 187 // Een tik (zonder slepen) vraagt hulp zonder dat er een post bij hoort. 188 buoy.addEventListener('click', (e) => { if (!e.detail || !dragging) askHelp(null, null); }); 189 190 function askHelp(noteId, who) { 191 document.getElementById('help-title').textContent = who 192 ? `Hulp vragen bij het bericht van ${who}?` : 'Hulp vragen aan je guardians?'; 193 dlg.dataset.note = noteId || ''; 194 document.getElementById('help-words').value = ''; 195 dlg.showModal(); 196 } 197 198 dlg.addEventListener('close', async () => { 199 if (dlg.returnValue !== 'send') return; 200 const body = { note: dlg.dataset.note || undefined, words: document.getElementById('help-words').value }; 201 const hint = document.getElementById('help-hint'); 202 try { 203 const r = await fetch('/ward/api/help', { 204 method: 'POST', 205 headers: { 'Content-Type': 'application/json' }, 206 body: JSON.stringify(body), 207 }); 208 const out = await r.json().catch(() => ({})); 209 hint.textContent = r.ok ? state.strings.helpSent 210 : (out.error === 'no_guardians' ? 'Je hebt nog geen guardians om te vragen.' : 'Dat lukte niet; probeer het nog eens.'); 211 } catch { 212 hint.textContent = 'Dat lukte niet; probeer het nog eens.'; 213 } 214 }); 215 } 216 100 217 /** De controle kost per guardian een remote fetch, dus hij komt ná het tekenen. */ 101 218 async function loadChecks() { … … 118 235 } 119 236 hint.textContent = `Gaat naar ${state.guardians.length} guardian(s).`; 237 btn.addEventListener('click', () => document.getElementById('buoy').click()); 120 238 } 121 239 122 240 renderGuardians(null); 241 wireBuoy(); 242 loadFeed(); 123 243 renderOffers(); 124 244 renderWaiting();
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)