Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/routes/guardian.js

    ree78e03 rfa33214  
    5757    'prop_line', 'prop_embeds', 'prop_play', 'prop_on', 'prop_off',
    5858    'prop_st_open', 'prop_st_accepted', 'prop_st_rejected', 'prop_st_expired',
    59     'panel_guards_far',
    60     // Het gate-paneel per ward (shaer-ahy.1): een rij per gate, met het soort en
    61     // de drempel erbij. De namen volgen de catalogus in gated.js.
    62     'gate_externalEmbeds', 'gate_externalPlayback', 'gate_follows',
    63     'gate_kind_setting', 'gate_kind_perRequest', 'gate_kind_handover',
    64     'gate_unknown', 'gate_threshold', 'gate_threshold_unknown',
    65     'gate_irreversible', 'gate_waiting', 'gate_blocked', 'gate_propose',
    66     // Oppikken en afhandelen van een hulpvraag (shaer-lgo).
    67     'help_pick', 'help_close', 'help_picked_by', 'help_handled_by', 'help_handled_note',
    68     'help_close_ask', 'help_close_yes', 'help_just_now', 'help_hours', 'help_days',
    69     'help_archive', 'help_archive_hide'];
     59    'panel_guards_far'];
    7060  const s = Object.fromEntries(keys.map((k) => [k, i18nT(L, `guardian.${k}`)]));
    7161  s.wave = i18nT(L, 'guardian.wave');
     
    8171            emoji_json, actor_emoji_json, media_json, quote_json, embed_json
    8272     FROM ap_mentions WHERE slug = ? AND help_request = 1 ORDER BY created_at DESC LIMIT 50`
    83   ).all(site.slug);
    84   // De gedeelde staat in EEN query (shaer-lgo): wie er al op af is en of het is
    85   // afgesloten. Per kaart vragen zou hier een N+1 opleveren, en dit is precies
    86   // het scherm dat een guardian in een haast openslaat.
    87   const helpStaat = Guardianship.help.statusFor(help.map((h) => h.object_uri));
    88   const helpItems = help.map((h) => ({
     73  ).all(site.slug).map((h) => ({
    8974    ...h,
    90     // Bij twijfel OPEN. Een hulpvraag die er afgehandeld uitziet terwijl hij dat
    91     // niet is, is de gevaarlijke fout -- niet andersom.
    92     state: helpStaat.get(h.object_uri) || { open: true, pickedUpBy: [], handled: null, ageMs: null },
    9375    // The dashboard is built in the browser, so it gets the body finished: the
    9476    // same partial de Krant and Berichten use. A 🛟 often carries a screenshot
     
    121103        status: Guardianship.gated.sentStatus(p, Date.now()),
    122104      })),
    123       // Alles wat voor dit kind gated is op EEN plek, met per gate het soort en
    124       // de drempel (shaer-ahy.1). Losse knoppen lieten een guardian zelf
    125       // uitzoeken wat er allemaal geldt; wat niet verstelbaar is stond nergens.
    126       gates: wardGates(site.slug, w.other_uri),
    127105    })),
    128106    offers: Guardianship.offersCollection(`${me}/queues/offers`, site.slug, me).orderedItems,
     
    135113      id: r.id, ward: r.ward_uri, proposer: r.proposer, feature: r.feature, value: !!r.value,
    136114    })),
    137     help: helpItems,
     115    help,
    138116    strings: uiStrings(L),
    139117  };
     
    214192  const site = siteForUser(req);
    215193  if (!site) return res.status(404).json({ error: 'no_site' });
    216   const L = resolveLang(req);
    217194  ensureWardConnections(site);
    218195  const wardUris = new Set(Guardianship.listWards(site.slug).map((w) => w.other_uri));
     
    232209      cw: p.cw || null,
    233210      media: p.media_json ? JSON.parse(p.media_json) : [],
    234       // Een post van je ward hoort er hetzelfde uit te zien als in de Krant en
    235       // in Berichten: dezelfde partial, dus opmaak, media, quote-kaart en
    236       // embed. Tot nu toe kreeg de PWA alleen kale content -- een guardian zag
    237       // een lege regel waar een foto stond. `content` blijft ernaast staan voor
    238       // een client die nog uit de cache draait.
    239       body_html: renderNoteBody(p, L),
    240211    }));
    241212  res.json({ items, following: wardUris.size });
     
    348319  if (!r) return res.status(502).json({ error: 'delivery' });
    349320  res.json({ ok: true, delivered: r.delivered });
    350 });
    351 
    352 // ── Een hulpvraag oppikken of afsluiten (shaer-lgo) ───────────────
    353 // Gaat naar de WARD en naar de MEDE-GUARDIANS. De ward hoort te weten dat er
    354 // iemand komt -- dat is de helft van de gerustheid -- en de anderen dat het
    355 // loopt, zodat niemand denkt dat de ander het al doet.
    356 //
    357 // OPPIKKEN mag stapelen: twee mensen die tegelijk reageren is geen probleem.
    358 // AFSLUITEN kent geen terugdraai; leeft de vraag nog, dan wordt hij opnieuw
    359 // gesteld. De stevige bevestiging zit in de client, net als bij het loslaten van
    360 // een ward: nooit een window.confirm.
    361 router.post('/api/help/:kind', requireAuth, express.json({ limit: '2kb' }), async (req, res) => {
    362   const site = siteForUser(req);
    363   if (!site) return res.status(404).json({ error: 'no_site' });
    364   const kind = req.params.kind === 'handled' ? 'handled' : 'pickup';
    365   const noteUri = String(req.body?.note || '').trim();
    366   const wardUri = String(req.body?.ward || '').trim();
    367   if (!noteUri || !/^https?:\/\//i.test(noteUri)) return res.status(400).json({ error: 'no_note' });
    368   // Alleen over een hulpvraag van een kind dat je echt bewaakt.
    369   const isWard = Guardianship.listWards(site.slug).some((w) => w.other_uri === wardUri);
    370   if (!isWard) return res.status(403).json({ error: 'not_your_ward' });
    371 
    372   const me = AP.actorId((process.env.PUBLIC_BASE_URL || '').replace(/\/+$/, ''), site.slug);
    373   // Onze eigen kopie meteen, zonder op bezorging te wachten: het scherm van
    374   // degene die klikt hoort niet te liegen omdat een andere server traag is.
    375   Guardianship.help.record(noteUri, me, kind, null);
    376 
    377   const anderen = Guardianship.listGuardians(wardUri.replace(/.*\/ap\/users\//, '')) || [];
    378   const ontvangers = [wardUri, ...anderen.map((g) => g.other_uri)].filter((u) => u && u !== me);
    379   const r = await AP.deliverDirectNote(site, {
    380     recipients: ontvangers,
    381     text: kind === 'handled' ? 'Deze hulpvraag is afgehandeld.' : 'Ik kijk hiernaar.',
    382     helpMark: { kind, noteUri },
    383   }).catch(() => null);
    384   // Bezorging kan mislukken; de eigen staat staat er dan toch. Dat melden we,
    385   // want "verstuurd" zeggen terwijl het niet aankwam is hier het ergste soort
    386   // stilte.
    387   res.json({ ok: true, delivered: r ? r.delivered : 0, recipients: ontvangers.length });
    388321});
    389322
     
    598531/** The playback gate of a ward we host (5.6): the heavier sibling. */
    599532function wardPlaybackSetting(uri) { return wardGateSetting(uri, 'external_playback'); }
    600 /**
    601  * De gate-rijen van een ward voor het paneel.
    602  *
    603  * De standen komen uit onze eigen kolommen als we het kind hosten; bij een ward
    604  * elders weten we ze niet en blijft het NULL -- onbekend, niet uit. Het aantal
    605  * guardians idem: dat wordt op de server van die ward bijgehouden, en zonder dat
    606  * getal wordt er geen drempel verzonnen.
    607  */
    608 function wardGates(mySlug, wardUri) {
    609   const statuses = wardGuardianStatuses(wardUri);
    610   const wachtend = Guardianship.follows.listReviewsByDirection(mySlug, 'incoming')
    611     .filter((r) => r.ward_uri === wardUri).length;
    612   return Guardianship.gated.gateRows({
    613     settings: {
    614       'shaer:externalEmbeds': wardEmbedSetting(wardUri),
    615       'shaer:externalPlayback': wardPlaybackSetting(wardUri),
    616     },
    617     guardianCount: statuses ? statuses.length : null,
    618     proposals: Guardianship.gated.listSent(mySlug, wardUri).map((p) => ({
    619       feature: p.feature, value: !!p.value, status: Guardianship.gated.sentStatus(p, Date.now()),
    620     })),
    621     waiting: { 'shaer:follows': wachtend || undefined },
    622   });
    623 }
    624 
    625533function wardGateSetting(uri, column) {
    626534  const base = (process.env.PUBLIC_BASE_URL || '').replace(/\/+$/, '');
Note: See TracChangeset for help on using the changeset viewer.