Changeset 6bc5ebd in Klonkt
- Timestamp:
- 08/06/2026 09:19:49 AM (5 weeks ago)
- Branches:
- main
- Children:
- 7842ca1
- Parents:
- 68a4d1b
- git-author:
- Robin <roboburr@…> (08/06/2026 09:19:48 AM)
- git-committer:
- Claude (agent) <aiclaude@…> (08/06/2026 09:19:49 AM)
- Files:
-
- 2 edited
-
src/services/ActivityPubService.js (modified) (4 diffs)
-
test/reactions-characterization.test.js (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/services/ActivityPubService.js
r68a4d1b r6bc5ebd 3439 3439 */ 3440 3440 /** 3441 * Van wat de client stuurde naar de canonieke sleutel voor een reactie. 3442 * 3443 * Een post heeft twee URI's: zijn AP-object-id (.../ap/notes/<uuid>) en zijn 3444 * leesbare permalink (.../effortlesseffect). De Krant en het C2S-pad spreken de 3445 * eerste, de interact-pagina de tweede. Werden reacties onder allebei opgeslagen, 3446 * dan bestond dezelfde like twee keer -- en erger: een like uit de Krant was op 3447 * de interact-pagina onzichtbaar, want daar werd op de permalink gezocht. 3448 * 3449 * Dit was de naad die fase 1 bewust open liet ("samentrekken is werk voor fase 3450 * 2"). Robin liep er meteen tegenaan: een geboost en geliket bericht toonde geen 3451 * highlight. Vandaar hier, en niet later. 3452 * 3453 * De object-URI wint, want dat is waar ap_timeline op sleutelt en waar de 3454 * backfill op is gebaseerd. Kennen we de post niet, dan blijft de invoer staan: 3455 * een reactie op iets buiten je tijdlijn moet gewoon werken. 3456 */ 3457 export function canonicalReactionUri(slug, uri) { 3458 if (!slug || !uri) return uri; 3459 try { 3460 if (db.prepare('SELECT 1 FROM ap_timeline WHERE slug = ? AND id = ?').get(slug, uri)) return uri; 3461 const row = db.prepare('SELECT id FROM ap_timeline WHERE slug = ? AND url = ? LIMIT 1').get(slug, uri); 3462 return (row && row.id) || uri; 3463 } catch { return uri; } 3464 } 3465 3466 /** 3441 3467 * Wat heb IK met dit object gedaan? Leest de tussentabel, de bron van waarheid 3442 3468 * sinds shaer-9e9 fase 2. Vervangt getMyReactions en getTimelineReaction, die … … 3445 3471 export function getReaction(slug, uri) { 3446 3472 try { 3447 const rows = (slug && uri) 3448 ? db.prepare('SELECT kind FROM ap_my_reactions WHERE site_slug = ? AND target_uri = ?').all(slug, uri) 3473 const key = canonicalReactionUri(slug, uri); 3474 const rows = (slug && key) 3475 ? db.prepare('SELECT kind FROM ap_my_reactions WHERE site_slug = ? AND target_uri = ?').all(slug, key) 3449 3476 : []; 3450 3477 return { liked: rows.some((r) => r.kind === 'like'), boosted: rows.some((r) => r.kind === 'boost') }; … … 3478 3505 export function setReaction(slug, uri, kind, on, opts = {}) { 3479 3506 if (!slug || !uri || (kind !== 'like' && kind !== 'boost')) return; 3480 setMyReaction(slug, uri, kind, !!on); 3481 const flagUri = opts.flagUri || uri; 3507 // EEN sleutel voor beide bronnen. opts.flagUri is de opgeloste object-URI van 3508 // de aanroeper (het C2S-pad kent die uit resolveRemoteNote en dat is 3509 // betrouwbaarder dan onze cache); anders leiden we hem af. Vroeger kreeg de 3510 // tussentabel de URI die de client stuurde en de vlag de opgeloste -- dat 3511 // maakte dezelfde like onvindbaar vanaf de andere pagina. 3512 const flagUri = opts.flagUri || canonicalReactionUri(slug, uri); 3513 setMyReaction(slug, flagUri, kind, !!on); 3482 3514 if (kind === 'boost') { 3483 3515 if (!on) unmarkBoosted(slug, flagUri); … … 4856 4888 gateOutgoingFollow, performApprovedFollow, 4857 4889 parseOwnPoll, pollTally, ownPollView, deliverPollUpdate, maybeCrawlThread, sendReport, localMentionSlugs, 4858 autoBoostCount, boostedCount, setReaction, getReaction, getReactionsFor, upsertBoostedNote, getCirkelPosts, getCirkelMembers, selfHealTimeline,4890 autoBoostCount, boostedCount, setReaction, getReaction, getReactionsFor, canonicalReactionUri, upsertBoostedNote, getCirkelPosts, getCirkelMembers, selfHealTimeline, 4859 4891 getNotifications, listBlocks, isBlockedAny, blockTarget, unblock, 4860 4892 deliverWithRetry, enqueueDelivery, processDeliveryQueue, startDeliveryWorker, -
test/reactions-characterization.test.js
r68a4d1b r6bc5ebd 193 193 }); 194 194 195 test('setReaction: gescheiden sleutels blijven werken zolang ze bestaan', () => {196 // De tussentabel wordt gesleuteld op wat de client stuurde, de vlag opde197 // opgeloste object-URI. Meestal gelijk, niet gegarandeerd. flagUri houdt dat198 // uit elkaar tot fase 2 ze samentrekt.195 test('setReaction: flagUri bepaalt de sleutel voor BEIDE bronnen', () => { 196 // Vroeger kreeg de tussentabel de URI die de client stuurde en de vlag de 197 // opgeloste. Nu is het er één, anders is dezelfde like onvindbaar vanaf een 198 // pagina die de andere URI kent. 199 199 const gestuurd = 'https://r.test/@anna/123'; 200 200 const opgelost = uri('sr3'); 201 201 seedTimeline(opgelost); 202 202 AP.setReaction('me', gestuurd, 'like', true, { flagUri: opgelost }); 203 assert.equal(AP.getMyReactions('me', gestuurd).liked, true, 'op de gestuurde uri'); 204 assert.equal(AP.getTimelineReaction('me', opgelost).liked, true, 'op de opgeloste uri'); 203 assert.equal(AP.getReaction('me', opgelost).liked, true, 'onder de opgeloste uri'); 204 assert.equal(AP.getTimelineReaction('me', opgelost).liked, true, 'en de vlag ook'); 205 assert.equal(AP.getMyReactions('me', gestuurd).liked, false, 'niet meer onder de gestuurde'); 205 206 }); 206 207 … … 273 274 } 274 275 }); 276 277 // ── De permalink en de object-URI zijn dezelfde post ───────────────────── 278 279 test('een like uit de Krant is zichtbaar op de interact-pagina (Robins melding)', () => { 280 // De Krant kent een post als AP-object-URI, de interact-pagina als permalink. 281 // Zochten die twee in verschillende sleutelruimtes, dan toonde een geboost en 282 // geliket bericht daar geen enkele highlight -- terwijl de reactie bestond. 283 const obj = uri('perm1'); 284 const permalink = 'https://sound-fabrics.com/effortlesseffect'; 285 db.prepare(`INSERT OR IGNORE INTO ap_timeline (id, slug, author_uri, author_name, content, url, created_at) 286 VALUES (?,?,?,?,?,?,?)`) 287 .run(obj, 'me', 'https://r.test/users/anna', 'Anna', '<p>x</p>', permalink, '2026-08-06 09:00:00'); 288 289 AP.setReaction('me', obj, 'like', true); // zoals de Krant het doet 290 AP.setReaction('me', obj, 'boost', true); 291 // en de interact-pagina vraagt het met de permalink: 292 assert.deepEqual(AP.getReaction('me', permalink), { liked: true, boosted: true }); 293 }); 294 295 test('andersom net zo: reageren via de permalink landt op de object-URI', () => { 296 const obj = uri('perm2'); 297 const permalink = 'https://sound-fabrics.com/tweede'; 298 db.prepare(`INSERT OR IGNORE INTO ap_timeline (id, slug, author_uri, author_name, content, url, created_at) 299 VALUES (?,?,?,?,?,?,?)`) 300 .run(obj, 'me', 'https://r.test/users/bo', 'Bo', '<p>y</p>', permalink, '2026-08-06 09:00:00'); 301 302 AP.setReaction('me', permalink, 'like', true); // zoals de interact-pagina het doet 303 assert.equal(AP.getReaction('me', obj).liked, true, 'onder de object-uri opgeslagen'); 304 assert.equal(AP.getTimelineReaction('me', obj).liked, true, 'dus de vlag landt ook goed'); 305 assert.equal(AP.getReaction('me', permalink).liked, true, 'en blijft vindbaar via de permalink'); 306 }); 307 308 test('een reactie op iets buiten je tijdlijn blijft gewoon werken', () => { 309 // Kennen we de post niet, dan is er niets te canoniseren en blijft de invoer 310 // de sleutel. Een like op een vreemde post mag daar niet op stuklopen. 311 const onbekend = 'https://elders.test/notes/xyz'; 312 AP.setReaction('me', onbekend, 'like', true); 313 assert.equal(AP.getReaction('me', onbekend).liked, true); 314 });
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)