Changeset 5218863 in Klonkt for src/assets/js/ward.js
- Timestamp:
- 08/12/2026 08:25:10 PM (3 months ago)
- Children:
- 4bb93aa
- Parents:
- 084b046
- File:
-
- 1 edited
-
src/assets/js/ward.js (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/assets/js/ward.js
r084b046 r5218863 231 231 const sky = new SkyRenderer(canvas, (i) => { 232 232 const c = convs[i]; 233 if (c) window.location.href = `/ward/thread?with=${encodeURIComponent(c.uri)}`; 233 if (c) openThread(c); 234 }); 235 236 /* ── Het gesprek: een overlay OVER de levende hemel ──────────────────── 237 Geen aparte pagina: de hemel eronder blijft dezelfde draaiende 238 instantie, precies zoals de thread in Shaer over de hemel schuift. */ 239 const thread = document.getElementById('thread'); 240 const list = document.getElementById('thread-list'); 241 let openWith = null; 242 let pollTimer = null; 243 244 async function loadThread() { 245 if (!openWith) return; 246 try { 247 const r = await fetch(`/ward/api/thread?with=${encodeURIComponent(openWith.uri)}`, { headers: { Accept: 'application/json' } }); 248 if (!r.ok) return; 249 const { items } = await r.json(); 250 list.replaceChildren(); 251 // De server geeft nieuwste eerst; een gesprek lees je oudste eerst. 252 [...items].reverse().forEach((m) => { 253 const b = el('div', `bubble ${m.dir}${m.wave ? ' wave' : ''}`); 254 const body = el('div', 'body'); 255 body.innerHTML = m.bodyHtml || ''; 256 b.append(body, el('span', 'when', m.whenText || '')); 257 list.append(b); 258 }); 259 list.scrollTop = list.scrollHeight; 260 } catch { /* volgende poll probeert opnieuw */ } 261 } 262 263 function openThread(c) { 264 openWith = c; 265 document.getElementById('thread-name').textContent = c.name; 266 thread.hidden = false; 267 loadThread(); 268 // Pollen zolang het gesprek open staat: eenvoudig en zuinig genoeg. 269 // Een socket kan later; het ritme van een gesprek is geen milliseconde. 270 clearInterval(pollTimer); 271 pollTimer = setInterval(loadThread, 8000); 272 } 273 274 function closeThread() { 275 openWith = null; 276 thread.hidden = true; 277 clearInterval(pollTimer); 278 } 279 document.getElementById('thread-close').addEventListener('click', closeThread); 280 document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && openWith) closeThread(); }); 281 282 document.getElementById('thread-form').addEventListener('submit', async (e) => { 283 e.preventDefault(); 284 const input = document.getElementById('thread-text'); 285 const text = input.value.trim(); 286 if (!text || !openWith) return; 287 input.value = ''; 288 await sendMessage(openWith.uri, text, false); 289 loadThread(); 290 }); 291 292 async function sendMessage(to, text, wave) { 293 try { 294 const r = await fetch('/ward/api/message', { 295 method: 'POST', 296 headers: { 'Content-Type': 'application/json' }, 297 body: JSON.stringify({ to, text, wave }), 298 }); 299 return r.ok; 300 } catch { return false; } 301 } 302 303 /* ── De zwaai-hand (§5) ──────────────────────────────────────────────── 304 Dezelfde hoek als de boei, en ze wisselen elkaar af met de stand: met de 305 kaarten verdwijnt de boei, op de hemel hangt de hand. Slepen op een 306 gezicht opent het paneel; de tekst die je kiest IS het bericht. */ 307 const hand = document.getElementById('hand'); 308 const waveDlg = document.getElementById('wave-dialog'); 309 let waveTarget = null; 310 311 (() => { 312 let dragging = false, dx = 0, dy = 0; 313 hand.addEventListener('pointerdown', (e) => { 314 dragging = true; 315 hand.setPointerCapture(e.pointerId); 316 const r = hand.getBoundingClientRect(); 317 dx = e.clientX - r.left; dy = e.clientY - r.top; 318 hand.classList.add('dragging'); 319 }); 320 hand.addEventListener('pointermove', (e) => { 321 if (!dragging) return; 322 hand.style.left = `${e.clientX - dx}px`; 323 hand.style.top = `${e.clientY - dy}px`; 324 hand.style.right = 'auto'; hand.style.bottom = 'auto'; 325 }); 326 const drop = (e) => { 327 if (!dragging) return; 328 dragging = false; 329 hand.classList.remove('dragging'); 330 hand.style.left = hand.style.top = 'auto'; 331 hand.style.right = ''; hand.style.bottom = ''; 332 // Zelfde rekensom als een tik op de hemel: het dichtstbijzijnde gezicht 333 // binnen bereik wint, en ernaast is bewust niets. 334 const r = canvas.getBoundingClientRect(); 335 const hit = sky.pick(e.clientX - r.left, e.clientY - r.top); 336 if (hit != null && convs[hit]) { 337 waveTarget = convs[hit]; 338 document.getElementById('wave-title').textContent = 339 state.strings.waveTo.replace('{who}', waveTarget.name); 340 waveDlg.showModal(); 341 } 342 }; 343 hand.addEventListener('pointerup', drop); 344 hand.addEventListener('pointercancel', drop); 345 })(); 346 347 waveDlg.addEventListener('click', async (e) => { 348 const btn = e.target.closest('button'); 349 if (!btn) return; 350 waveDlg.close(); 351 if (btn.id === 'wave-cancel' || !waveTarget) return; 352 const isWave = btn.dataset.wave === '1'; 353 const text = isWave ? '👋' : btn.dataset.text; 354 if (await sendMessage(waveTarget.uri, text, isWave)) openThread(waveTarget); 234 355 }); 235 356 … … 244 365 canvas.style.pointerEvents = on ? 'auto' : 'none'; 245 366 document.body.classList.toggle('messages-mode', on); 367 document.getElementById('buoy').hidden = on; 368 hand.hidden = !on; 369 if (!on) closeThread(); 246 370 btn.textContent = on ? state.strings.toCards : state.strings.toSky; 247 371 btn.setAttribute('aria-pressed', String(on));
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)