Changes in src/assets/js/guardian.js [1ff801d:d56d471] in Klonkt
- File:
-
- 1 edited
-
src/assets/js/guardian.js (modified) (10 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/assets/js/guardian.js
r1ff801d rd56d471 49 49 // child's panel. 50 50 var HELP_TOP = 5; 51 52 /**53 * Wacht deze hulpvraag nog op iemand?54 *55 * EEN plek, met opzet. Dit werd eerst op twee plekken los beslist -- de lijst56 * en de teller op de wardregel -- en toen de lijst gefilterd werd bleef die57 * teller een afgehandelde vraag meetellen. Een derde plek komt er vast, en dan58 * hoort hij hier langs te komen.59 *60 * Bij TWIJFEL open: alles wat geen expliciete afsluiting draagt telt als61 * wachtend. De omgekeerde fout -- iets als afgehandeld tonen dat het niet is --62 * is hier de gevaarlijke.63 */64 function helpOpen(h) { return !h || !h.state || h.state.open; }65 51 66 52 function helpCard(h) { … … 86 72 card.appendChild(a); 87 73 } 88 card.appendChild(helpState(h));89 74 return card; 90 }91 92 /** Hoe lang geleden, grof. Een oppik vervalt niet maar hoort wel te verouderen. */93 function ago(ms) {94 var u = Math.floor(ms / 3600000);95 if (u < 1) return T.help_just_now || 'just now';96 if (u < 24) return (T.help_hours || '{n}h ago').replace('{n}', u);97 return (T.help_days || '{n}d ago').replace('{n}', Math.floor(u / 24));98 }99 100 /**101 * De gedeelde staat van een hulpvraag (shaer-lgo): wie er al op af is, of het102 * is afgesloten, en de twee knoppen.103 *104 * De faalstand hier is NIET veilig: 'iedereen denkt dat het geregeld is' is105 * gevaarlijker dan geen markering. Dus bij twijfel toont dit OPEN, en een oude106 * oppik verkleurt in plaats van te verdwijnen -- anders ziet een hulpvraag er107 * onaangeroerd uit terwijl er iemand mee bezig is.108 */109 function helpState(h) {110 var st = h.state || { open: true, pickedUpBy: [], handled: null, ageMs: null };111 var box = el('div', 'g-help-state');112 113 if (st.handled) {114 var wie = st.handled.handle || handleOf(st.handled.uri);115 box.appendChild(el('div', 'g-help-done', (T.help_handled_by || 'Handled by {who}').replace('{who}', wie)));116 // Geen terugdraaiknop: leeft de vraag nog, dan wordt hij opnieuw gesteld.117 box.appendChild(el('p', 'small g-empty', T.help_handled_note || ''));118 return box;119 }120 121 (st.pickedUpBy || []).forEach(function (p) {122 var line = (T.help_picked_by || '{who} is looking into this').replace('{who}', p.handle || handleOf(p.uri));123 box.appendChild(el('div', 'g-help-pick', line));124 });125 // Oud, maar niet weg. Dit is het verschil tussen 'er is iemand mee bezig' en126 // 'er was ooit iemand mee bezig'.127 if (st.ageMs != null && st.ageMs > 3600000) {128 box.appendChild(el('div', 'g-help-age small', ago(st.ageMs)));129 }130 131 var row = el('div', 'row');132 var mij = (st.pickedUpBy || []).some(function (p) { return p.uri === S.me; });133 if (!mij) {134 var pick = el('button', 'small', T.help_pick || 'I am on it');135 pick.addEventListener('click', function () { markHelp(h, 'pickup', pick); });136 row.appendChild(pick);137 }138 var done = el('button', 'quiet small', T.help_close || 'Mark handled');139 // Een stevige bevestiging, en nooit een window.confirm: dat verstopt een140 // uitleg achter een OK die mensen wegklikken. Zelfde lijn als het loslaten141 // van een ward.142 done.addEventListener('click', function () {143 done.hidden = true;144 var ask = el('div', 'g-confirm');145 ask.appendChild(el('p', 'small', T.help_close_ask || ''));146 var ja = el('button', 'small', T.help_close_yes || 'Yes, handled');147 ja.addEventListener('click', function () { markHelp(h, 'handled', ja); });148 var nee = el('button', 'quiet small', T.release_no || 'No');149 nee.addEventListener('click', function () { ask.remove(); done.hidden = false; });150 ask.appendChild(ja); ask.appendChild(nee);151 box.appendChild(ask);152 });153 row.appendChild(done);154 box.appendChild(row);155 return box;156 }157 158 function markHelp(h, kind, btn) {159 if (btn) btn.disabled = true;160 fetch('/guardian/api/help/' + kind, {161 method: 'POST', headers: { 'Content-Type': 'application/json' },162 body: JSON.stringify({ note: h.object_uri, ward: h.actor_uri, site: S.site }),163 }).then(refresh).catch(function () { if (btn) btn.disabled = false; });164 75 } 165 76 … … 167 78 var list = document.getElementById('help-list'); 168 79 list.textContent = ''; 169 var alle = S.help || []; 170 // Afgehandeld hoort niet meer in de lijst die om aandacht vraagt, en niet 171 // meer in de teller: anders blijft het cijfer alarm slaan voor iets dat af 172 // is, en neemt een gesloten vraag de ruimte in van een open vraag. 173 var open = alle.filter(helpOpen); 174 var klaar = alle.filter(function (h) { return !helpOpen(h); }); 175 176 open.slice(0, HELP_TOP).forEach(function (h) { list.appendChild(helpCard(h)); }); 177 if (open.length > HELP_TOP) { 178 list.appendChild(el('p', 'g-sec-sub', '+ ' + (open.length - HELP_TOP) + ' — ' + (T.panel_help || ''))); 179 } 180 181 // WEG is niet hetzelfde als AF. Een afgehandelde hulpvraag blijft bestaan -- 182 // er wordt in dit systeem niets herschreven -- maar hij hoort achter een 183 // klik, niet voor je neus. De volledige geschiedenis per kind staat sowieso 184 // in het paneel van dat kind. 185 if (klaar.length) { 186 var doos = el('div', 'g-help-archive'); 187 doos.hidden = true; 188 klaar.forEach(function (h) { doos.appendChild(helpCard(h)); }); 189 var knop = el('button', 'quiet small', (T.help_archive || '{n} handled').replace('{n}', klaar.length)); 190 knop.addEventListener('click', function () { 191 doos.hidden = !doos.hidden; 192 knop.textContent = doos.hidden 193 ? (T.help_archive || '{n} handled').replace('{n}', klaar.length) 194 : (T.help_archive_hide || 'hide'); 195 }); 196 list.appendChild(knop); 197 list.appendChild(doos); 198 } 199 80 var help = S.help || []; 81 help.slice(0, HELP_TOP).forEach(function (h) { list.appendChild(helpCard(h)); }); 82 if (help.length > HELP_TOP) { 83 list.appendChild(el('p', 'g-sec-sub', '+ ' + (help.length - HELP_TOP) + ' — ' + (T.panel_help || ''))); 84 } 200 85 var badge = document.getElementById('help-count'); 201 badge.textContent = open.length; badge.hidden = open.length === 0; 202 // "Geen hulpverzoeken. Mooi zo." mag ook staan als er wel een archief is: 203 // er wacht dan immers niets. 204 show('help-empty', open.length === 0); 86 badge.textContent = help.length; badge.hidden = help.length === 0; 87 show('help-empty', help.length === 0); 205 88 } 206 89 … … 468 351 } 469 352 470 /** Het woord dat we voor een gate gebruiken; onbekend valt terug op de naam. */471 function gateLabel(feature) {472 return T['gate_' + feature.replace('shaer:', '')] || feature.replace('shaer:', '');473 }474 475 /**476 * Een rij in het gate-paneel. Toont WAT er geldt, van welk SOORT het is, welke477 * drempel er hoort en wat er loopt -- en pas daarna een knop, als er iets te478 * verzetten valt.479 *480 * Het soort staat erbij omdat de gates niet hetzelfde werken: een stand is aan481 * of uit, volgverzoeken zijn een stroom beslissingen, en een overdracht is482 * onomkeerbaar zodra het kind hem gebruikt. Vier rijen met dezelfde schakelaar483 * zouden dat verschil wegpoetsen.484 */485 function gateRow(w, g) {486 var row = el('div', 'g-gate');487 var head = el('div', 'g-gate-head');488 head.appendChild(el('span', 'g-gate-name', gateLabel(g.feature)));489 head.appendChild(el('span', 'g-gate-kind', T['gate_kind_' + g.kind] || g.kind));490 row.appendChild(head);491 492 // De stand. NULL is onbekend en dat is iets anders dan uit: bij een ward op493 // een andere server wordt die kolom daar bijgehouden.494 var stand = g.value === null || g.value === undefined495 ? (T.gate_unknown || 'unknown')496 : (g.value ? (T.prop_on || 'on') : (T.prop_off || 'off'));497 var meta = el('div', 'g-gate-meta small');498 meta.appendChild(el('span', null, stand));499 if (g.threshold) {500 meta.appendChild(el('span', null, (T.gate_threshold || '{need} of {of} guardians')501 .replace('{need}', g.threshold.need).replace('{of}', g.threshold.of)));502 } else {503 // Geen drempel verzinnen die we niet kennen.504 meta.appendChild(el('span', 'g-dim', T.gate_threshold_unknown || ''));505 }506 if (!g.reversible) meta.appendChild(el('span', 'g-gate-warn', T.gate_irreversible || ''));507 if (g.waiting) {508 meta.appendChild(el('span', 'g-gate-wait', (T.gate_waiting || '{n} waiting').replace('{n}', g.waiting)));509 }510 row.appendChild(meta);511 512 if (g.proposal) {513 row.appendChild(el('p', 'small g-prop g-prop-' + g.proposal.status,514 (T.prop_line || 'Proposal {what} {value}: {status}')515 .replace('{what}', gateLabel(g.feature))516 .replace('{value}', g.proposal.value ? (T.prop_on || 'on') : (T.prop_off || 'off'))517 .replace('{status}', T['prop_st_' + g.proposal.status] || g.proposal.status)));518 }519 if (g.blockedBy) {520 row.appendChild(el('p', 'small g-empty', (T.gate_blocked || 'needs {what} first')521 .replace('{what}', gateLabel(g.blockedBy))));522 }523 if (g.adjustable) {524 row.appendChild(gateButton(w, g.feature, g.value, T.gate_propose || T.embeds_propose,525 T.prop_on || 'on', T.prop_off || 'off'));526 }527 return row;528 }529 530 353 function wardPanel(w) { 531 354 var uri = w.other_uri; … … 535 358 var set = el('div', 'g-panel-sec'); 536 359 set.appendChild(el('h3', null, T.settings_title || 'Settings')); 537 // Een rij per gate, uit de catalogus van de server (shaer-ahy.1). Losse 538 // knoppen lieten een guardian zelf uitzoeken wat er allemaal geldt, en wat 539 // niet verstelbaar is stond nergens -- terwijl dat de helft van het antwoord 540 // is op "wat mag dit kind". 541 (w.gates || []).forEach(function (g) { set.appendChild(gateRow(w, g)); }); 542 // Terugval voor een server die de catalogus nog niet stuurt: dan de twee 543 // knoppen zoals ze waren, zodat een oudere Klonkt niet met een leeg vak zit. 544 if (!(w.gates || []).length) { 545 var setRow = el('div', 'row'); 546 setRow.appendChild(gateButton(w, 'shaer:externalEmbeds', w.embeds, T.embeds_propose, T.embeds_on, T.embeds_off)); 547 if (w.embeds !== false) { 548 setRow.appendChild(gateButton(w, 'shaer:externalPlayback', w.playback, T.play_propose, T.play_on, T.play_off)); 549 } 550 set.appendChild(setRow); 551 (w.proposals || []).forEach(function (p) { 552 var what = p.feature === 'shaer:externalPlayback' ? (T.prop_play || 'playback') : (T.prop_embeds || 'link previews'); 553 set.appendChild(el('p', 'small g-prop g-prop-' + p.status, (T.prop_line || 'Proposal {what} {value}: {status}') 554 .replace('{what}', what).replace('{value}', p.value ? (T.prop_on || 'on') : (T.prop_off || 'off')) 555 .replace('{status}', T['prop_st_' + p.status] || p.status))); 556 }); 557 } 360 var setRow = el('div', 'row'); 361 setRow.appendChild(gateButton(w, 'shaer:externalEmbeds', w.embeds, T.embeds_propose, T.embeds_on, T.embeds_off)); 362 // The heavier sibling (5.6): seeing that a video exists is one decision, 363 // letting a third party's player run inside the app is another. Hidden 364 // only when previews are known-OFF: for a ward on another server the 365 // value is unknown, and unknown is not off. Hiding it there meant a 366 // guardian elsewhere could never even propose playback, which is how a 367 // whole proposal round went into the wrong gate. The ward's server 368 // enforces play-needs-previews at serve time regardless. 369 if (w.embeds !== false) { 370 setRow.appendChild(gateButton(w, 'shaer:externalPlayback', w.playback, T.play_propose, T.play_on, T.play_off)); 371 } 372 set.appendChild(setRow); 373 // What this guardian proposed and how it stands (5.6). This used to be a 374 // button caption that vanished on refresh, so a running decision was 375 // invisible: you could not tell "waiting", "done" and "expired" apart. 376 (w.proposals || []).forEach(function (p) { 377 var what = p.feature === 'shaer:externalPlayback' ? (T.prop_play || 'playback') : (T.prop_embeds || 'link previews'); 378 var line = (T.prop_line || 'Proposal {what} {value}: {status}') 379 .replace('{what}', what) 380 .replace('{value}', p.value ? (T.prop_on || 'on') : (T.prop_off || 'off')) 381 .replace('{status}', T['prop_st_' + p.status] || p.status); 382 set.appendChild(el('p', 'small g-prop g-prop-' + p.status, line)); 383 }); 558 384 panel.appendChild(set); 559 385 … … 594 420 T.panel_follow_empty || '', followCard); 595 421 596 // Hier juist de VOLLEDIGE geschiedenis van dit kind -- dat is waar dit597 // paneel voor is. Wel wat nog wacht bovenaan, want dat is waar je naar zoekt598 // als je het opent.599 422 sectionInto(panel, T.panel_help || 'Calls for help', 600 (S.help || []).filter(function (h) { return h.actor_uri === uri; }) 601 .sort(function (a, b) { return (helpOpen(b) ? 1 : 0) - (helpOpen(a) ? 1 : 0); }), 423 (S.help || []).filter(function (h) { return h.actor_uri === uri; }), 602 424 T.panel_help_empty || '', helpCard); 603 425 … … 631 453 actRow.appendChild(rel); 632 454 act.appendChild(actRow); 633 // Bovenaan, niet onderaan: zwaaien en loslaten zijn de dingen die je DOET. 634 // De rest van het paneel is lezen -- instellingen, wie er nog meer op let, 635 // wat er binnenkwam. Wie het paneel opent om iets te doen hoorde eerst langs 636 // vijf secties te scrollen. 637 panel.insertBefore(act, panel.firstChild); 455 panel.appendChild(act); 638 456 return panel; 639 457 } … … 649 467 row.appendChild(el('span', 'who grow', handleOf(uri, w.other_handle))); 650 468 // Counts on the row: whatever is waiting must be visible with the panel shut. 651 // Alleen wat nog WACHT, zoals het commentaar hierboven al zei: een 652 // afgehandelde vraag hoorde de boei niet te laten staan. 653 var nHelp = (S.help || []).filter(function (h) { return h.actor_uri === uri && helpOpen(h); }).length; 469 var nHelp = (S.help || []).filter(function (h) { return h.actor_uri === uri; }).length; 654 470 var nFollow = FOLLOWS.filter(function (f) { return f.wardUri === uri; }).length; 655 471 if (nHelp) row.appendChild(el('span', 'tag help', '🛟 ' + nHelp)); … … 737 553 card.appendChild(head); 738 554 var body = el('div', 'feed-body'); 739 // body_html is de gedeelde note-body-partial, serverside gerenderd: opmaak,740 // media, quote-kaart en embed, precies als in de Krant en in Berichten.741 // Valt terug op de kale content voor een client uit de cache.742 var html = p.body_html || p.content || '';743 555 if (p.cw) { 744 // De content warning blijft van de PWA zelf: note-body versluiert alleen745 // bij nsfw, en een ward-post met alleen een cw hoort hier dicht te staan.746 556 var d = document.createElement('details'); 747 557 var sum = document.createElement('summary'); sum.textContent = p.cw; d.appendChild(sum); 748 var inner = el('div'); inner.innerHTML = html; d.appendChild(inner);558 var inner = el('div'); inner.innerHTML = p.content || ''; d.appendChild(inner); 749 559 body.appendChild(d); 750 560 } else { 751 body.innerHTML = html; // server-sanitized HTML (same as Berichten)561 body.innerHTML = p.content || ''; // server-sanitized HTML (same as Berichten) 752 562 } 753 563 card.appendChild(body); … … 890 700 891 701 renderAll(); pushState(); loadFeed(); loadFollowReqs(); 892 893 // De push die de melding brengt is meteen het teken dat de staat veranderd is. 894 // Daarmee hoeft er geen tweede, open verbinding bij: hetzelfde kanaal doet het 895 // werk, en het werkt ook als de app dicht is. 896 if ('serviceWorker' in navigator) { 897 navigator.serviceWorker.addEventListener('message', function (e) { 898 if (e.data && e.data.klonkt === 'push') refresh(); 899 }); 900 } 901 // Het tikje blijft als vangnet -- niet elke verandering geeft een melding, en 902 // niet iedereen heeft meldingen aanstaan. Maar niet tikken terwijl niemand 903 // kijkt: dat waren verzoeken voor een tabblad op de achtergrond. Bij terugkomen 904 // meteen een keer, want dan is de kans op nieuws het grootst. 905 setInterval(function () { if (!document.hidden) refresh(); }, 45000); 906 document.addEventListener('visibilitychange', function () { if (!document.hidden) refresh(); }); 702 setInterval(refresh, 45000); // live-ish while open 907 703 } catch (e) { 908 704 fatal((e && e.message) || String(e));
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)