Changeset 6fab3f3 in Klonkt
- Timestamp:
- 08/08/2026 10:42:20 PM (4 weeks ago)
- Branches:
- main
- Children:
- cae7bfc
- Parents:
- 060d4a5
- git-author:
- Robin <roboburr@…> (08/08/2026 10:42:18 PM)
- git-committer:
- roboburr <roboburr@…> (08/08/2026 10:42:20 PM)
- Files:
-
- 1 added
- 3 edited
-
src/assets/js/guardian.js (modified) (2 diffs)
-
src/routes/guardian.js (modified) (1 diff)
-
src/services/ActivityPubService.js (modified) (5 diffs)
-
test/guardian-longpoll.test.js (added)
Legend:
- Unmodified
- Added
- Removed
-
src/assets/js/guardian.js
r060d4a5 r6fab3f3 967 967 } 968 968 969 // ── De lange poll (Barts opdracht, 9-8) ────────────────────────────── 970 // 971 // Het paneel tikte elke 45 seconden, ongeacht of er iets gebeurd was. Nu hangt 972 // er een verzoek open tot er iets is dat je moet verwerken -- een aanbod, een 973 // volgverzoek, een gate-voorstel, een hulpvraag. Dat is niet alleen zuiniger, 974 // het is ook SNELLER: een hulproep stond eerst tot drie kwartier van een 975 // minuut op het scherm te wachten. 976 // 977 // 304 = stilte, en dan meteen opnieuw wachten. Alleen bij 200 wordt er iets 978 // opnieuw getekend. 979 var pollBezig = false; 980 function longPoll() { 981 if (pollBezig || document.hidden) return; 982 pollBezig = true; 983 fetch('/guardian/api/state?wait=25&site=' + encodeURIComponent(S.site)) 984 .then(function (r) { 985 pollBezig = false; 986 if (r.status === 304) return null; // stilte 987 return r.json().then(function (s) { 988 if (s && !s.error) { S = s; T = s.strings || T; renderAll(); } 989 return loadFeed().then(loadFollowReqs); 990 }); 991 }) 992 .then(function () { setTimeout(longPoll, 200); }) 993 .catch(function () { 994 // Netwerk weg of server herstart: niet meteen opnieuw beuken. 995 pollBezig = false; 996 setTimeout(longPoll, 5000); 997 }); 998 } 999 969 1000 // ── 2. Adopt ─────────────────────────────────────────────────────────── 970 1001 var form = document.getElementById('adopt-form'); … … 1072 1103 // kijkt: dat waren verzoeken voor een tabblad op de achtergrond. Bij terugkomen 1073 1104 // meteen een keer, want dan is de kans op nieuws het grootst. 1074 setInterval(function () { if (!document.hidden) refresh(); }, 45000); 1075 document.addEventListener('visibilitychange', function () { if (!document.hidden) refresh(); }); 1105 // De lange poll doet het werk. Het tikje blijft als vangnet, maar veel trager: 1106 // valt er een wekker weg (een pad dat niet wekt, een herstart midden in een 1107 // verzoek), dan mag het scherm niet voorgoed stilstaan. Vijf minuten in plaats 1108 // van vijfenveertig seconden, en met de ETag kost zo'n tik meestal een lege 304. 1109 longPoll(); 1110 setInterval(function () { if (!document.hidden) refresh(); }, 300000); 1111 document.addEventListener('visibilitychange', function () { 1112 if (document.hidden) return; 1113 refresh(); 1114 longPoll(); // bij terugkomst weer een wachter openzetten 1115 }); 1076 1116 } catch (e) { 1077 1117 fatal((e && e.message) || String(e)); -
src/routes/guardian.js
r060d4a5 r6fab3f3 182 182 183 183 // ── JSON state for refreshes ───────────────────────────────────────────── 184 router.get('/api/state', requireAuth, (req, res) => { 185 const site = siteForUser(req); 186 if (!site) return res.status(404).json({ error: 'no_site' }); 187 // Het paneel tikt elke 45 seconden, of er nu iets gebeurd is of niet. Met een 188 // ETag kost stilte een lege 304 in plaats van het hele paneel (Barts punt, 9-8). 189 return AP.sendMaybe304(req, res, dashboardState(site, resolveLang(req)), { contentType: 'application/json' }); 184 /** 185 * De staat van het paneel, desgewenst als LANGE POLL (Barts opdracht, 9-8). 186 * 187 * Zonder `wait` gedraagt de route zich exact zoals altijd. Met `wait` blijft het 188 * antwoord hangen tot er iets gebeurt dat de guardian moet verwerken, of tot de 189 * tijd om is -- dan een lege 304. 190 * 191 * EERST KIJKEN, DAN WACHTEN. Veranderde er iets tussen het vorige antwoord en 192 * dit verzoek, dan is de merksteen nu al anders en gaat het antwoord METEEN de 193 * deur uit. Zou je eerst gaan wachten, dan blijft nieuws dat net in dat gaatje 194 * viel vijfentwintig seconden liggen -- en juist bij een hulpvraag is dat de 195 * verkeerde vertraging. 196 * 197 * WAKKER OP ALLES. De guardianship-module zendt veertien soorten gebeurtenissen 198 * uit en die wekken allemaal (wakeGuardian); daarnaast wekt de tijdlijn (onNews), 199 * want de berichten van je wards staan in ditzelfde scherm. 200 */ 201 router.get('/api/state', requireAuth, async (req, res) => { 202 const site = siteForUser(req); 203 if (!site) return res.status(404).json({ error: 'no_site' }); 204 const stuur = () => AP.sendMaybe304(req, res, dashboardState(site, resolveLang(req)), { contentType: 'application/json' }); 205 206 const wachtS = Math.min(Math.max(parseInt(req.query.wait, 10) || 0, 0), 50); 207 const merk = req.headers['if-none-match']; 208 if (!wachtS || !merk) return stuur(); 209 210 // Is er nu al iets anders? Dan niet wachten. 211 const nu = AP.etagFor(JSON.stringify(dashboardState(site, resolveLang(req)))); 212 if (nu !== merk) return stuur(); 213 214 await new Promise((klaar) => { 215 let af = false; 216 const eind = () => { if (af) return; af = true; clearTimeout(t); offG(); offN(); klaar(); }; 217 const offG = AP.onGuardian(site.slug, eind); 218 const offN = AP.onNews(site.slug, eind); 219 const t = setTimeout(eind, wachtS * 1000); 220 // Hing de client op, dan houdt niemand dit antwoord meer vast. 221 res.on('close', eind); 222 }); 223 if (res.writableEnded) return undefined; 224 return stuur(); 190 225 }); 191 226 -
src/services/ActivityPubService.js
r060d4a5 r6fab3f3 1860 1860 return () => { set.delete(cb); if (!set.size) _newsWaiters.delete(slug); }; 1861 1861 } 1862 /** 1863 * Wachters op het Guardian-paneel (Barts opdracht, 9-8). 1864 * 1865 * APART VAN onNews, en dat is met opzet. `news` gaat over de tijdlijn; dit gaat 1866 * over alles wat een guardian te VERWERKEN krijgt -- een aanbod, een 1867 * volgverzoek, een gate-voorstel, een hulpvraag, een lapse. De guardianship- 1868 * module zendt daar al veertien soorten voor uit; die gingen alleen naar push, 1869 * en push kiest bewust maar een handvol. Het paneel moet ze allemaal weten. 1870 * 1871 * Een wachter wordt EEN keer gewekt en daarna vergeten: het antwoord dat volgt 1872 * is de nieuwe waarheid, en de client komt terug met een nieuwe wachter. 1873 */ 1874 const _guardWaiters = new Map(); // slug -> Set<cb> 1875 export function onGuardian(slug, cb) { 1876 let set = _guardWaiters.get(slug); 1877 if (!set) { set = new Set(); _guardWaiters.set(slug, set); } 1878 set.add(cb); 1879 return () => { set.delete(cb); if (!set.size) _guardWaiters.delete(slug); }; 1880 } 1881 export function wakeGuardian(slug) { 1882 const set = _guardWaiters.get(slug); 1883 if (!set || !set.size) return; 1884 const cbs = [...set]; 1885 set.clear(); 1886 _guardWaiters.delete(slug); 1887 for (const cb of cbs) { try { cb(); } catch { /* een wachter mag de rest nooit breken */ } } 1888 } 1889 1862 1890 export function wakeNews(slug) { 1863 1891 const set = _newsWaiters.get(slug); … … 2387 2415 const ai = actorInfo(await resolveActor(actorUri).catch(() => null), actorUri); 2388 2416 Guardianship.help.record(mark.noteUri, actorUri, mark.kind, ai && ai.handle); 2417 wakeGuardian(slug); // een mede-guardian pakte iets op: het paneel hoort het meteen 2389 2418 console.log('[AP] help', mark.kind, actorUri, '→', mark.noteUri); 2390 2419 } … … 2429 2458 if (!mijn) { console.warn('[AP] gate request from someone who is not our ward, ignored:', actorUri, '→', slug); continue; } 2430 2459 Guardianship.gatereq.record(slug, actorUri, req.feature, o.id); 2460 wakeGuardian(slug); // het kind vroeg om een poort 2431 2461 console.log('[AP] gate request', req.feature, actorUri, '→', slug); 2432 2462 } … … 5970 6000 // De labels hangen aan dezelfde sleutels als het Guardian-paneel, zodat een 5971 6001 // melding en het scherm waar hij heen wijst hetzelfde woord gebruiken. 5972 onEvent: (slug, ev) => { 5973 const p = guardianEventPush(slug, ev); 5974 if (p) pushEvent(slug, p); 5975 }, 6002 onEvent: (slug, ev) => onGuardianshipEvent(slug, ev), 5976 6003 }); 6004 6005 /** 6006 * Wat er gebeurt als de guardianship-module iets uitzendt. 6007 * 6008 * TWEE VERSCHILLENDE VRAGEN, en ze horen niet dezelfde te zijn: wie maak je 6009 * WAKKER (push kiest bewust een handvol soorten), en wat moet een scherm dat 6010 * openstaat WETEN (alles). Het paneel werd daarom voorheen niet gewekt door de 6011 * tien soorten zonder pushtekst -- die zag je pas bij de volgende tik. 6012 * 6013 * Apart en met een naam, zodat een toets erbij kan. Verstopt in de deps-literal 6014 * was hij onbereikbaar, en een mutatie die het wekken weghaalde bleef groen. 6015 */ 6016 export function onGuardianshipEvent(slug, ev) { 6017 wakeGuardian(slug); 6018 const p = guardianEventPush(slug, ev); 6019 if (p) pushEvent(slug, p); 6020 return p; 6021 } 5977 6022 5978 6023 /** … … 6066 6111 getNotifications, listBlocks, isBlockedAny, blockTarget, unblock, 6067 6112 deliverWithRetry, enqueueDelivery, processDeliveryQueue, startDeliveryWorker, 6068 sendMaybe304, etagFor, proposeGate, getReplyUris, getThread, filterThreadToCircle, gateAttachments, stripEmojiTags, markNotificationsSeen, countUnseenNotifications, hasPlayableAudio,6113 sendMaybe304, etagFor, onGuardian, wakeGuardian, onGuardianshipEvent, proposeGate, getReplyUris, getThread, filterThreadToCircle, gateAttachments, stripEmojiTags, markNotificationsSeen, countUnseenNotifications, hasPlayableAudio, 6069 6114 linkifyBody, bakePostContent, bakePostContentWithMentions, listFollowers, removeFollower, listConnections, 6070 6115 noteVisibility, belongsInTimeline, playerUrlFor, isRejectedObject, rejectInteraction, interactionReportTarget,
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)