Changeset 189e335 in Klonkt for src/routes/posts.js


Ignore:
Timestamp:
08/06/2026 07:02:57 AM (5 weeks ago)
Author:
Claude (agent) <aiclaude@…>
Branches:
main
Children:
84ce988
Parents:
99a7b40
git-author:
Robin <roboburr@…> (08/06/2026 07:02:56 AM)
git-committer:
Claude (agent) <aiclaude@…> (08/06/2026 07:02:57 AM)
Message:

Antwoorden vanuit een gesprek, rijk, met de zwaai als eigen knop

Berichten kon je gesprekken tonen maar er niet in antwoorden. Het invoerveld dat
er stond was msg-quickreply: platte tekst, 200 tekens, en het verstuurde geen
antwoord maar een zwaai (deliverDirectNote met wave:true, een FEP-633c-seintje).
Dat is iets anders dat toevallig op een reply leek.

Onder elke draad staat nu de gedeelde reply-editor, ingeklapt achter een
"Reageer" -- open onder elk gesprek maakt de lijst weer onleesbaar, precies wat
deze weergave moest oplossen. De zwaai blijft ernaast als eigen knop, want een
seintje en een antwoord horen niet op een hoop.

/messages/reply kiest het pad op wat de draad zelf meedraagt (replyTo):

  • draad aan een post -> deliverReply op het nieuwste ontvangen bericht erin, met een controle dat die parent ook echt bij die post hoort; anders kon een aangepast formulier een antwoord onder andermans draad hangen.
  • draad aan een persoon -> een direct bericht terug.

Voor dat tweede pad kon deliverDirectNote nog geen rijke inhoud: het escapete
platte tekst. Het accepteert nu html, door dezelfde sanitizer als deliverReply,
zodat een antwoord uit Berichten via één poort gaat. De mention-anker blijft een
eigen alinea vooraan, want de ontvanger moet genoemd staan ook als de inhoud met
een kop of lijst begint. Levert de sanitizer niets bruikbaars op, dan valt het
terug op de escaped tekst -- een leeggepoetste editor mag geen leeg bericht
versturen.

Meldingen in nl/en/de erbij, en getNotifications geeft nu het interactie-id en
de actor-uri mee, want zonder die twee weet een antwoord niet waar het heen moet.

Geverifieerd op dev: beide draden daar adresseren hun eigen tegenpartij, het
formulier draagt de juiste verborgen velden en de zwaai staat er los naast.
Suite 412/412.

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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/routes/posts.js

    r99a7b40 r189e335  
    967967});
    968968
     969// Antwoorden vanuit een gesprek in Berichten. Twee paden, en welke het wordt
     970// bepaalt de draad zelf (zie groupConversations → replyTo):
     971//   - hangt de draad aan een post van jou, dan is dit een gewone reply op het
     972//     nieuwste ontvangen bericht erin: deliverReply, publiek zoals de thread;
     973//   - hangt hij aan een persoon, dan is het een direct bericht terug.
     974// Rijk in beide gevallen: `content` is de HTML uit de reply-editor, `text` de
     975// platte versie die de editor er altijd bij levert (en die het no-JS-formulier
     976// als enige stuurt).
     977router.post('/messages/reply', requireSiteManager, async (req, res) => {
     978  const site = res.locals.site;
     979  const back = `${res.locals.siteUrlBase || ''}/messages`;
     980  if (!site) return res.status(404).send('Site required');
     981  const text = String(req.body.text || '');
     982  const html = String(req.body.content || '');
     983  let attachments = [];
     984  try { attachments = JSON.parse(req.body.attachments || '[]'); } catch { /* geen media */ }
     985  let mentions;
     986  try { if (req.body.mentions !== undefined) mentions = JSON.parse(req.body.mentions || '[]'); } catch { mentions = undefined; }
     987  const language = String(req.body.language || '');
     988  // Leeg is leeg: een bericht zonder tekst EN zonder media is geen bericht.
     989  if (!text.trim() && !html.trim() && !attachments.length) return res.redirect(back + '?error=reply_empty');
     990
     991  const interactionId = parseInt(req.body.interaction_id, 10) || 0;
     992  const postSlug = String(req.body.post_slug || '');
     993  const toActor = String(req.body.to || '');
     994  try {
     995    if (interactionId && postSlug) {
     996      const post = db.prepare('SELECT id, slug FROM posts WHERE site_id = ? AND slug = ?').get(site.id, postSlug);
     997      const parent = ActivityPubService.getInteractionById(interactionId);
     998      // De parent MOET bij deze post horen: anders zou een gemanipuleerd
     999      // formulier een antwoord onder andermans draad kunnen hangen.
     1000      if (!post || !parent || parent.post_id !== post.id) return res.redirect(back + '?error=reply_target');
     1001      await ActivityPubService.deliverReply(site, {
     1002        postId: post.id, postSlug: post.slug, parent, text, html, attachments, mentions, language,
     1003      });
     1004    } else if (/^https?:\/\//i.test(toActor)) {
     1005      const r = await Guardianship.deliverDirectNote(site, { recipients: [toActor], text, html, language, attachments });
     1006      if (!r) return res.redirect(back + '?error=reply_failed');
     1007    } else {
     1008      return res.redirect(back + '?error=reply_target');
     1009    }
     1010  } catch (e) {
     1011    console.warn('[AP] reply from Berichten failed:', e.message);
     1012    return res.redirect(back + '?error=reply_failed');
     1013  }
     1014  res.redirect(back + '?success=reply_sent');
     1015});
     1016
    9691017router.get('/fediverse', requireSiteManager, (req, res) => res.redirect(`${res.locals.siteUrlBase || ''}/messages`));
    9701018
Note: See TracChangeset for help on using the changeset viewer.