Changeset 55eca8b in Klonkt
- Timestamp:
- 07/30/2026 12:49:18 PM (6 weeks ago)
- Branches:
- main
- Children:
- 1a2f206
- Parents:
- 6a99668
- Files:
-
- 3 edited
-
src/routes/activitypub.js (modified) (2 diffs)
-
src/services/ActivityPubService.js (modified) (2 diffs)
-
test/sent-notes.test.js (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
src/routes/activitypub.js
r6a99668 r55eca8b 280 280 }, 281 281 })); 282 // Inbound REPLIES on your own posts: stored as interactions (the web's 283 // comment machinery), never as mentions, so this read missed them and a 284 // friend's reply arrived everywhere except in your app (Robins melding, 285 // 30-7). Same shape as the other legs; media/quotes ride the stored JSON. 286 const replies = AP.getReplyMessages(auth.site.slug, 60).map((m) => ({ 287 id: `${m.object_uri}#create`, 288 type: 'Create', 289 actor: m.actor_uri, 290 published: AP.isoStamp(m.published || m.created_at), 291 object: { 292 id: m.object_uri, 293 type: 'Note', 294 attributedTo: m.actor_uri, 295 content: AP.stripLeadingMentions(m.content), 296 inReplyTo: m.parent_uri || `${base}/ap/notes/${m.post_id}`, 297 published: AP.isoStamp(m.published || m.created_at), 298 to: [me], 299 tag: [{ type: 'Mention', href: me, name: myHandle }, ...(AP.timelineEmojis(m.emoji_json) || [])], 300 attachment: AP.timelineAttachments(m.media_json), 301 'shaer:quote': AP.timelineQuote(m.quote_json), 302 'shaer:author': (m.actor_name || m.actor_handle || m.actor_icon) ? { 303 name: m.actor_name || undefined, handle: m.actor_handle || undefined, 304 icon: m.actor_icon || undefined, url: m.actor_url || undefined, 305 emojis: (() => { try { return m.actor_emoji_json ? JSON.parse(m.actor_emoji_json) : undefined; } catch { return undefined; } })(), 306 } : undefined, 307 'shaer:embed': embedsAllowed ? AP.timelineEmbed(m.embed_json, { playback: playbackAllowed }) : undefined, 308 }, 309 })); 282 310 // Your OWN sent notes (replies and direct messages, ap_outbox): without 283 311 // them a reply existed everywhere except in your own app, Messages showed … … 295 323 })); 296 324 // Newest first over all legs, so the app can keep treating this as one feed. 297 const items = [...posts, ...messages, ... sent].sort((a, b) => String(b.published || '').localeCompare(String(a.published || '')));325 const items = [...posts, ...messages, ...replies, ...sent].sort((a, b) => String(b.published || '').localeCompare(String(a.published || ''))); 298 326 AP.sendAP(res, { 299 327 '@context': AP.AP_CONTEXT, -
src/services/ActivityPubService.js
r6a99668 r55eca8b 2900 2900 * skipped here and stay a post. 2901 2901 */ 2902 // Inbound replies on YOUR posts, for the app's message stream. They live in 2903 // ap_interactions (the web's comment machinery) and deliberately NOT in 2904 // ap_mentions (the mention store returns early for replies-to-us), so the 2905 // C2S read missed them entirely: a reply arrived at the other side 2906 // everywhere EXCEPT in the other's app (Robins melding, 30-7: "komt niet 2907 // binnen bij de ander"). 2908 export function getReplyMessages(slug, limit) { 2909 try { 2910 return db.prepare(` 2911 SELECT i.object_uri, i.actor_uri, i.actor_name, i.actor_handle, i.actor_icon, i.actor_url, 2912 i.content, i.published, i.created_at, i.parent_uri, i.post_id, 2913 i.emoji_json, i.actor_emoji_json, i.media_json, i.quote_json, i.embed_json 2914 FROM ap_interactions i 2915 JOIN posts p ON p.id = i.post_id 2916 JOIN sites s ON s.id = p.site_id 2917 WHERE s.slug = ? AND i.kind = 'reply' 2918 ORDER BY COALESCE(i.published, i.created_at) DESC LIMIT ?`).all(slug, limit || 60); 2919 } catch { return []; } 2920 } 2921 2902 2922 export function getDirectMessages(slug, limit) { 2903 2923 try { … … 4200 4220 linkifyBody, bakePostContent, bakePostContentWithMentions, listFollowers, removeFollower, listConnections, 4201 4221 noteVisibility, belongsInTimeline, playerUrlFor, isRejectedObject, rejectInteraction, interactionReportTarget, 4202 getMessages, notificationsSeenAt, ingestOutboxActivity, c2sVisibility, actorDisplay, buildActorRef, prefersEnriched, selfAuthor, 4222 getMessages, notificationsSeenAt, ingestOutboxActivity, c2sVisibility, actorDisplay, buildActorRef, prefersEnriched, selfAuthor, getReplyMessages, 4203 4223 }; -
test/sent-notes.test.js
r6a99668 r55eca8b 44 44 }); 45 45 46 test("an inbound reply on your post reaches the app's message stream", () => { 47 // The other half of "komt niet binnen bij de ander": inbound replies live 48 // in ap_interactions (web comments), never in ap_mentions, so the C2S read 49 // never served them. getReplyMessages is the leg that does. 50 db.prepare(`INSERT INTO posts (id, site_id, author_id, slug, title, content, status, published_at, created_at, updated_at) 51 VALUES ('p9','s1','u1','n-p9','', '<p>x</p>','published',datetime('now'),datetime('now'),datetime('now'))`).run(); 52 db.prepare(`INSERT INTO ap_interactions (kind, post_id, object_uri, actor_uri, actor_name, actor_handle, content, published, parent_uri, visibility, media_json, created_at) 53 VALUES ('reply','p9','https://unresolvable.invalid/notes/77','https://unresolvable.invalid/u/ness','Ness','@ness@unresolvable.invalid','<p>hoi terug</p>','2026-07-30T12:00:00Z','https://klonkt.test/ap/notes/p9','followers','[{"url":"https://unresolvable.invalid/m/f.jpg","type":"image/jpeg"}]',CURRENT_TIMESTAMP)`).run(); 54 const rows = AP.getReplyMessages('me', 60); 55 const r = rows.find((x) => x.object_uri === 'https://unresolvable.invalid/notes/77'); 56 assert.ok(r, 'the reply is served for the post owner'); 57 assert.equal(r.parent_uri, 'https://klonkt.test/ap/notes/p9', 'threads under the post'); 58 assert.equal(r.actor_handle, '@ness@unresolvable.invalid'); 59 assert.ok(r.media_json.includes('f.jpg'), 'its media rides along'); 60 assert.equal(AP.getReplyMessages('bestaat-niet', 60).length, 0, 'and only for the post owner'); 61 }); 62 46 63 test('a duplicate reply is idempotent success with the SAME id, not a 502', async () => { 47 64 const first = await AP.deliverReply(site, { postId: '', postSlug: null, parent, text: 'nogmaals', visibility: 'friends' });
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)