source: Klonkt/src/views/pages/ward.ejs@ a5a6249

Last change on this file since a5a6249 was a5a6249, checked in by Robin <roboburr@…>, 3 months ago

De Ward PWA: de feed en de reddingsboei

Plak 2. Nu valt er iets te doen op /ward in plaats van alleen te kijken.

DE FEED. Dezelfde bron als de Krant (getTimeline) en dezelfde partial
voor de body (renderNoteBody), zodat een post er overal hetzelfde uitziet:
opmaak, media, quote-kaart, embed. Alleen kale content doorgeven zou een
lege regel opleveren waar een foto hoort, precies de fout die het
guardian-dashboard ooit had.

DE BOEI, en die is hier BETER dan in de app. Shaer maakt een
schermafdruk van de kaart, met al het gedoe van buiten beeld renderen.
Op de server weten we wélke note het is, dus de hulpvraag draagt de note
zelf mee. Een guardian ziet daardoor de echte post en niet een plaatje
dat er ooit op leek.

Slepen met pointer-events, want dat is het enige dat op een telefoon en
met een muis hetzelfde doet. touch-action: none op de boei houdt de
pagina stil tijdens het slepen; zonder dat vecht het met scrollen en
verlies je hem halverwege. De kaart eronder licht op, want mikken met een
vinger zonder terugkoppeling is gokken. Een tik zonder slepen vraagt hulp
zonder dat er een post bij hoort, en de grote knop bovenaan doet
hetzelfde.

Changed files:
src/routes/ward.js

  • /api/feed met afgemaakte body per kaart
  • /api/help: direct bericht aan ALLE guardians, met de note erbij; 409 als er geen guardians zijn, want dan is er niets te vragen

src/views/pages/ward.ejs

  • feed-sectie, zwevende boei, hulpdialoog

src/assets/js/ward.js

  • kaarten tekenen, slepen, mikken, versturen

src/assets/css/ward.css

  • kaarten, boei, dialoog, sr-only

src/services/i18n.js

  • acht nieuwe sleutels in nl, en, de

test/ward-pwa.test.js

  • de feed levert een gerenderde body; de boei weigert zonder guardians; een vreemde kan hem niet afvuren

remarks: 889 tests groen. Slepen is alleen in code getest, niet met een
echte vinger op een echt scherm; dat is aan Robin op dev.

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

  • Property mode set to 100644
File size: 2.7 KB
RevLine 
[303bfc4]1<!DOCTYPE html>
2<html lang="<%= lang %>">
3<head>
4 <meta charset="utf-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
6 <title><%= state.strings.title %></title>
7 <link rel="manifest" href="/ward/manifest.webmanifest">
8 <link rel="icon" href="/ward/icon.svg" type="image/svg+xml">
9 <meta name="theme-color" content="#5a32e6">
10 <link rel="stylesheet" href="/ward/app.css">
11</head>
12<body>
13 <main id="app">
14 <h1><%= state.strings.title %></h1>
15
16 <!-- De hulpknop staat BOVENAAN en niet onderaan bij de instellingen.
17 Als je hem nodig hebt, moet je niet hoeven zoeken. -->
18 <section class="help">
19 <button id="help-btn" type="button"><%= state.strings.help %></button>
20 <p class="hint" id="help-hint"></p>
21 </section>
22
[a5a6249]23 <section id="feed">
24 <h2><%= state.strings.feed %></h2>
25 <div class="cards" id="card-list"></div>
26 <p class="empty" id="feed-empty" hidden><%= state.strings.feedEmpty %></p>
27 </section>
28
[303bfc4]29 <section id="guardians">
30 <h2><%= state.strings.guardians %></h2>
31 <ul class="list" id="guardian-list"></ul>
32 <p class="empty" id="guardians-empty" hidden><%= state.strings.noGuardians %></p>
33 </section>
34
35 <section id="offers" hidden>
36 <h2><%= state.strings.offers %></h2>
37 <ul class="list" id="offer-list"></ul>
38 </section>
39
40 <section id="waiting" hidden>
41 <h2><%= state.strings.waiting %></h2>
42 <ul class="list" id="waiting-list"></ul>
43 </section>
44 </main>
45
[a5a6249]46 <!-- De reddingsboei zweeft boven de pagina en is sleepbaar, net als in
47 Shaer. Een knop, geen plaatje: dan werkt hij ook met een toetsenbord en
48 leest een schermlezer hem voor. -->
49 <button id="buoy" type="button" aria-describedby="buoy-hint">🛟</button>
50 <p id="buoy-hint" class="sr-only"><%= state.strings.buoyHint %></p>
51
52 <dialog id="help-dialog">
53 <form method="dialog">
54 <h2 id="help-title"></h2>
55 <label for="help-words"><%= state.strings.helpWords %></label>
56 <textarea id="help-words" rows="3" maxlength="500"></textarea>
57 <menu>
58 <button value="cancel"><%= state.strings.cancel %></button>
59 <button id="help-send" value="send" class="primary"><%= state.strings.helpSend %></button>
60 </menu>
61 </form>
62 </dialog>
63
[303bfc4]64 <!-- De begintoestand reist als JSON mee, zodat de pagina meteen compleet is
65 en niet eerst leeg staat te wachten op een fetch. Geen inline script:
66 CSP staat dat niet toe, dus het gaat als data door een script-tag met
67 een type dat de browser niet uitvoert. -->
68 <script type="application/json" id="ward-state"><%- JSON.stringify(state) %></script>
69 <script src="/ward/app.js" defer></script>
70</body>
71</html>
Note: See TracBrowser for help on using the repository browser.