Changeset de89079 in Klonkt


Ignore:
Timestamp:
07/26/2026 10:56:43 AM (6 weeks ago)
Author:
Robin Genis <roboburr@…>
Branches:
main
Children:
c628dcd4
Parents:
42e7616
Message:

C2S: liked/boosted op de inbox-read + followers-only reply-adressering

Voor de Shaer-detailweergave (Like/Boost/Reply): de inbox-read serveert nu per
note shaer:liked/shaer:boosted (uit ap_timeline.liked/boosted), zodat de app de
knop-status toont en kan togglen/undoen. En een reply kan followers-only: als de
client de reply als .friends stuurt, respecteert de outbox dat nu (de auteur in
to + onze volgers in cc, GEEN Public). De standaard-reply blijft quiet-public.

hasGuardians blijft ongewijzigd automatisch gestempeld (keuze Robin).

Changed files:
src/routes/activitypub.js

  • inbox-read: shaer:liked + shaer:boosted per note

src/services/ActivityPubService.js

  • buildNote(isReply): visibility 'friends' -> cc zonder Public (followers-only)
  • deliverReply accepteert visibility en bewaart 'friends' op de ap_outbox-rij
  • ingestOutboxActivity geeft c2sVisibility(object) door aan de reply

New file:
test/c2s-reply-visibility.test.js

  • followers-only reply (auteur+followers, geen Public), quiet-public default, direct-note

remarks: 190 tests groen (was 187).

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

Files:
1 added
2 edited

Legend:

Unmodified
Added
Removed
  • src/routes/activitypub.js

    r42e7616 rde89079  
    191191        emojis: (() => { try { return t.reblog_emoji_json ? JSON.parse(t.reblog_emoji_json) : undefined; } catch { return undefined; } })(),
    192192      } : undefined,
     193      // Whether THIS account already liked/boosted the note, so the app's
     194      // detail-view buttons show the current state (and can toggle/undo).
     195      'shaer:liked': !!t.liked,
     196      'shaer:boosted': !!t.boosted,
    193197    },
    194198  }));
  • src/services/ActivityPubService.js

    r42e7616 rde89079  
    278278        ? (JSON.parse(post.to_actors || '[]'))
    279279        : (post.to_actor ? [post.to_actor] : [PUBLIC]),
    280       cc: post.visibility === 'direct' ? [] : [PUBLIC, `${meR}/followers`],
     280      // Followers-only reply ('friends', shaer detail-view Reply): the parent
     281      // author (in `to`) + our followers, but NO Public — it does not federate
     282      // into open discovery. Default reply stays quiet-public (Public in cc).
     283      cc: post.visibility === 'direct' ? []
     284        : post.visibility === 'friends' ? [`${meR}/followers`]
     285          : [PUBLIC, `${meR}/followers`],
    281286      // FEP-633c 5.2.1: a ward's call for help. Only ever on direct notes.
    282287      ...Guardianship.helpRequestProps(post),
     
    21452150          const parent = await resolveRemoteNote(c2sIdOf(object.inReplyTo)).catch(() => null);
    21462151          if (!parent) return { status: 502, error: 'cannot_resolve_inReplyTo' };
    2147           const r = await deliverReply(site, { postId: parent.localPostId || '', postSlug: null, parent, text: plain });
     2152          // Honour the client's visibility for the reply: 'friends' (followers-
     2153          // only, the Shaer detail-view Reply) drops Public; anything else stays
     2154          // quiet-public. 'direct' was already handled above.
     2155          const r = await deliverReply(site, { postId: parent.localPostId || '', postSlug: null, parent, text: plain, visibility: c2sVisibility(object) });
    21482156          if (!r || !r.id) return { status: 502, error: 'reply_failed' };
    21492157          return { status: 201, id: r.id, url: `${base}/ap/notes/${r.id}` };
     
    22572265// Send a reply FROM this site to a remote actor (in reply to their inbound reply).
    22582266// `parent` = an ap_interactions row (actor_uri, actor_url, actor_handle, object_uri).
    2259 export async function deliverReply(site, { postId, postSlug, parent, text, html, language, attachments, mentions }) {
     2267export async function deliverReply(site, { postId, postSlug, parent, text, html, language, attachments, mentions, visibility }) {
    22602268  const base = (process.env.PUBLIC_BASE_URL || '').replace(/\/+$/, '');
    22612269  // Rich replies: `html` is the reply editor's HTML (sanitized here); `text` is
     
    23282336  const id = crypto.randomUUID();
    23292337  iStmts().insO.run(id, site.slug, postId, postSlug || null, parent.object_uri || null, toActorUri, toHandle, content, replyLang, mediaJson);
     2338  // Followers-only reply (shaer detail-view): mark the row so buildNote drops
     2339  // Public from cc. Default (undefined/'public'/'quiet') stays quiet-public.
     2340  if (visibility === 'friends') { try { db.prepare('UPDATE ap_outbox SET visibility = ? WHERE id = ?').run('friends', id); } catch { /* ignore */ } }
    23302341  const row = iStmts().getO.get(id);
    23312342  const note = buildReplyNote(base, site, row);
Note: See TracChangeset for help on using the changeset viewer.