Changeset 5412cbb in Klonkt for src/assets/js


Ignore:
Timestamp:
08/12/2026 08:38:08 PM (3 months ago)
Author:
Robin <roboburr@…>
Children:
2086ea2
Parents:
4bb93aa
Message:

Ward-PWA: de (+) voor een nieuw gesprek

De hemel toont alleen met wie je al sprak; de (+) is de deur naar de
rest. Hij zit op de OORSPRONG van het veld en pant en zoomt met de
gezichten mee, zodat hij altijd het midden van de vliegende avatars is,
waar de hemel ook heen drijft. Precies waar de plaatsing bewust ruimte
vrijhoudt: elk gezicht hangt minstens 1.4 cel naar buiten.

De tik kiest eerst een gezicht en pas daarna de (+), met een raakzone
die meeschaalt met de zoom. De kiezer erachter toont iedereen met een
volgrelatie, welke kant op dan ook: dezelfde bron als de Connect-pagina
(listConnections), want de vraag is dezelfde. Kies iemand en het lege
gesprek opent gewoon; het eerste bericht maakt het echt.

Changed files:
src/routes/ward.js

  • /api/friends: following + followers samengevoegd, met naam en icoon

src/assets/js/ward-sky.js

  • de (+) getekend op de veldoorsprong; onNew naast onPick

src/assets/js/ward.js

  • de vriendenkiezer, die doorvalt in openThread

src/views/pages/ward.ejs + src/assets/css/ward.css

  • de kiezer als dialoog

src/services/i18n.js

  • twee sleutels in nl, en, de

test/ward-pwa.test.js

  • beide richtingen zijn een deur, en elke deur heeft een naam

remarks: 899 tests groen.

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

Location:
src/assets/js
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • src/assets/js/ward-sky.js

    r4bb93aa r5412cbb  
    152152   * @param {(index:number)=>void} onPick  een gezicht aangetikt
    153153   */
    154   constructor(canvas, onPick) {
     154  constructor(canvas, onPick, onNew) {
    155155    this.canvas = canvas;
    156156    this.g = canvas.getContext('2d');
    157157    this.onPick = onPick;
     158    // De (+) in het midden: de deur naar iemand NIEUW. Hij zit op de
     159    // OORSPRONG van het veld en pant en zoomt met de gezichten mee, zodat hij
     160    // altijd het midden van de vliegende avatars is, waar de hemel ook heen
     161    // drijft. De plaatsing houdt daar bewust ruimte vrij (elk gezicht hangt
     162    // minstens 1.4 cel naar buiten).
     163    this.onNew = onNew;
    158164    this.faces = [];
    159165    this.spots = [];
    … …  
    211217    const s = Math.min(Math.max(this.scale, 0.5), 1.6);
    212218    const draw = FACE * s;
     219
     220    // De (+) eerst, dan de gezichten eroverheen als het krap wordt.
     221    if (this.interactive && this.onNew) {
     222      const px = cx + this.panX;
     223      const py = cy + this.panY;
     224      const pr = 22 * s;
     225      g.beginPath(); g.arc(px, py, pr, 0, Math.PI * 2);
     226      g.fillStyle = 'rgba(255,255,255,0.92)'; g.fill();
     227      g.strokeStyle = '#11131a'; g.lineWidth = Math.max(2, 3 * s); g.lineCap = 'round';
     228      g.beginPath();
     229      g.moveTo(px - pr * 0.45, py); g.lineTo(px + pr * 0.45, py);
     230      g.moveTo(px, py - pr * 0.45); g.lineTo(px, py + pr * 0.45);
     231      g.stroke();
     232    }
    213233
    214234    for (let i = 0; i < this.spots.length; i++) {
    … …  
    248268      if (moved < 6) {
    249269        const r = c.getBoundingClientRect();
    250         const hit = nearestFace(e.clientX - r.left, e.clientY - r.top, this.spots, {
     270        const x = e.clientX - r.left;
     271        const y = e.clientY - r.top;
     272        const hit = nearestFace(x, y, this.spots, {
    251273          cx: this.w / 2, cy: this.h / 2, cell: CELL, scale: this.scale,
    252274          panX: this.panX, panY: this.panY,
    253275        });
    254         if (hit != null) this.onPick?.(hit);
     276        if (hit != null) { this.onPick?.(hit); return; }
     277        // Geen gezicht? Dan misschien de (+): zelfde middelpunt als getekend,
     278        // met een raakzone die meeschaalt zodat mikken bij elke zoom klopt.
     279        const s2 = Math.min(Math.max(this.scale, 0.5), 1.6);
     280        const d = Math.hypot(x - (this.w / 2 + this.panX), y - (this.h / 2 + this.panY));
     281        if (d <= 26 * s2) this.onNew?.();
    255282      }
    256283    };
  • src/assets/js/ward.js

    r4bb93aa r5412cbb  
    232232    const c = convs[i];
    233233    if (c) openThread(c);
    234   });
     234  }, () => openFriendsPicker());
     235
     236  /* ── De (+): de deur naar iemand nieuw ─────────────────────────────────
     237     De hemel toont alleen met wie je al sprak; de kiezer toont iedereen met
     238     een volgrelatie. Kies iemand en het lege gesprek opent gewoon: het
     239     eerste bericht maakt het echt. */
     240  const friendsDlg = document.getElementById('friends-dialog');
     241  async function openFriendsPicker() {
     242    const list = document.getElementById('friends-list');
     243    list.replaceChildren();
     244    friendsDlg.showModal();
     245    try {
     246      const r = await fetch('/ward/api/friends', { headers: { Accept: 'application/json' } });
     247      const items = r.ok ? (await r.json()).items || [] : [];
     248      document.getElementById('friends-empty').hidden = items.length > 0;
     249      items.forEach((f) => {
     250        const li = el('li', 'card friend');
     251        const row = el('button', 'row');
     252        row.type = 'button';
     253        if (f.icon) {
     254          const img = el('img', 'avatar');
     255          img.src = f.icon; img.alt = ''; img.loading = 'lazy';
     256          row.append(img);
     257        }
     258        const kolom = el('span', 'wie');
     259        kolom.append(el('span', 'name', f.name), el('span', 'hint', f.handle));
     260        row.append(kolom);
     261        row.addEventListener('click', () => {
     262          friendsDlg.close();
     263          openThread({ uri: f.uri, name: f.name, icon: f.icon });
     264        });
     265        li.append(row);
     266        list.append(li);
     267      });
     268    } catch { /* de kiezer blijft leeg staan; annuleren kan altijd */ }
     269  }
     270  document.getElementById('friends-cancel').addEventListener('click', () => friendsDlg.close());
    235271
    236272  /* ── Het gesprek: een overlay OVER de levende hemel ────────────────────
Note: See TracChangeset for help on using the changeset viewer.