Ignore:
Timestamp:
07/21/2026 11:10:32 PM (7 weeks ago)
Author:
Robin Genis <roboburr@…>
Branches:
main
Children:
4c70ecb
Parents:
e2ea5c4
Message:

C2S inbox carries friends' media as AS2 attachments

Shaer showed images on own posts (outbox serialises attachment) but not on
friends' posts: the C2S inbox GET built its Create(Note) items without the
stored media. The timeline rows already keep media_json ([{url, type}],
written on the inbound Create); it just never reached the wire. A new pure
helper maps it to an AS2 attachment array so clients render friends' images
and audio exactly like own outbox posts.

Changed files:
src/services/ActivityPubService.js

  • timelineAttachments(mediaJson): media_json -> AS2 attachment array (Document + mediaType + url); empty or malformed JSON yields undefined and never blocks the item
  • exported alongside getTimeline

src/routes/activitypub.js

  • inbox GET: object.attachment = AP.timelineAttachments(t.media_json)

test/c2s-inbox.test.js

  • covers the mapping, the unknown-mediaType case, dropped rows without url, and malformed media_json (148 tests total, all green)

Alt text is not in media_json yet, so attachment.name stays empty for now.

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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/services/ActivityPubService.js

    re2ea5c4 rfb30b5d  
    24342434export function getTimeline(slug, limit, offset) { return tlStmts().list.all(slug, limit || 50, offset || 0); }
    24352435
     2436// Inbox C2S read: a timeline row's media_json ([{url, type}], written on the
     2437// inbound Create) → AS2 `attachment` array, so a client (Shaer) can render a
     2438// friend's images/audio/video natively, exactly like own outbox posts. The
     2439// stored `type` is the mediaType and may be ''. Malformed JSON yields
     2440// undefined and never blocks the item.
     2441export function timelineAttachments(mediaJson) {
     2442  try {
     2443    const list = mediaJson ? JSON.parse(mediaJson) : [];
     2444    const rows = (Array.isArray(list) ? list : [])
     2445      .filter((m) => m && m.url)
     2446      .map((m) => ({ type: 'Document', mediaType: m.type || undefined, url: m.url }));
     2447    return rows.length ? rows : undefined;
     2448  } catch { return undefined; }
     2449}
     2450
    24362451// ── Cirkel = posts from the accounts you auto-boost ("feature an artist") ──
    24372452let _abCount, _cirkelPosts, _cirkelMembers;
     
    30903105  getInteractions, getInteractionById, setInteractionBoosted, setInteractionLiked, setMyReaction, getMyReactions, buildReplyNote, getOutboxNote, deliverReply, resolveRemoteNote,
    30913106  listOutbox, deliverOutboxDelete, deliverOutboxUpdate, deliverDirectNote,
    3092   webfingerResolve, followActor, resolveRemoteActor, unfollowActor, listFollowing, setAutoBoost, backfillFromOutbox, getTimeline, sendInteraction, voteOnPoll, voteOnRemotePoll,
     3107  webfingerResolve, followActor, resolveRemoteActor, unfollowActor, listFollowing, setAutoBoost, backfillFromOutbox, getTimeline, timelineAttachments, sendInteraction, voteOnPoll, voteOnRemotePoll,
    30933108  parseOwnPoll, pollTally, ownPollView, deliverPollUpdate, maybeCrawlThread, sendReport, localMentionSlugs,
    30943109  autoBoostCount, boostedCount, markBoosted, unmarkBoosted, markLiked, unmarkLiked, getTimelineReaction, upsertBoostedNote, getCirkelPosts, getCirkelMembers, selfHealTimeline,
Note: See TracChangeset for help on using the changeset viewer.