Changeset 14f7cb2 in Klonkt
- Timestamp:
- 08/06/2026 09:10:41 AM (5 weeks ago)
- Branches:
- main
- Children:
- 68a4d1b
- Parents:
- 0e27e54
- git-author:
- Robin <roboburr@…> (08/06/2026 09:10:40 AM)
- git-committer:
- Claude (agent) <aiclaude@…> (08/06/2026 09:10:41 AM)
- Files:
-
- 4 edited
-
src/routes/activitypub.js (modified) (2 diffs)
-
src/routes/posts.js (modified) (5 diffs)
-
src/services/ActivityPubService.js (modified) (2 diffs)
-
test/reactions-characterization.test.js (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
src/routes/activitypub.js
r0e27e54 r14f7cb2 285 285 const playbackAllowed = embedsAllowed 286 286 && Guardianship.externalPlaybackAllowed(auth.site.external_playback, isWard); 287 const posts = AP.getTimeline(auth.site.slug, 60).map((t) => ({ 287 const rows = AP.getTimeline(auth.site.slug, 60); 288 // Eén query voor de hele pagina (shaer-9e9 fase 2): shaer:liked komt uit de 289 // tussentabel, de bron van waarheid, en niet meer uit de afgeleide kolom op 290 // ap_timeline. Per rij vragen zou hier een N+1 opleveren. 291 const reacties = AP.getReactionsFor(auth.site.slug, rows.map((t) => t.id)); 292 const posts = rows.map((t) => ({ 288 293 id: `${t.id}#create`, 289 294 type: 'Create', … … 333 338 // Whether THIS account already liked/boosted the note, so the app's 334 339 // detail-view buttons show the current state (and can toggle/undo). 335 'shaer:liked': !! t.liked,336 'shaer:boosted': !! t.boosted,340 'shaer:liked': !!(reacties.get(t.id) || {}).liked, 341 'shaer:boosted': !!(reacties.get(t.id) || {}).boosted, 337 342 // An external (non-fediverse) embed, thumbnail-only and never an iframe. 338 343 // Omitted entirely when the gate is closed (see above). -
src/routes/posts.js
r0e27e54 r14f7cb2 806 806 liked: !!req.query.liked, 807 807 boosted: !!req.query.boosted, 808 reacted: (site && uri) ? ActivityPubService.get MyReactions(site.slug, uri) : { liked: false, boosted: false },808 reacted: (site && uri) ? ActivityPubService.getReaction(site.slug, uri) : { liked: false, boosted: false }, 809 809 siteTitle: site ? site.title : '', 810 810 }); … … 839 839 let on = false; 840 840 if (site && uri) { 841 on = !ActivityPubService.get MyReactions(site.slug, uri).liked;841 on = !ActivityPubService.getReaction(site.slug, uri).liked; 842 842 ActivityPubService.resolveRemoteNote(uri) 843 843 .then((note) => note && ActivityPubService.sendInteraction(site, on ? 'like' : 'unlike', note.object_uri || uri, note.actor_uri)) … … 857 857 let on = false; 858 858 if (site && uri) { 859 on = !ActivityPubService.get MyReactions(site.slug, uri).boosted;859 on = !ActivityPubService.getReaction(site.slug, uri).boosted; 860 860 ActivityPubService.resolveRemoteNote(uri) 861 861 .then((note) => { … … 1280 1280 let on = false; 1281 1281 if (site && note) { 1282 on = !ActivityPubService.get TimelineReaction(site.slug, note).liked;1282 on = !ActivityPubService.getReaction(site.slug, note).liked; 1283 1283 try { await ActivityPubService.sendInteraction(site, on ? 'like' : 'unlike', note, (req.body.author || '').toString()); } catch (e) { /* ignore */ } 1284 1284 ActivityPubService.setReaction(site.slug, note, 'like', on); … … 1294 1294 let on = false; 1295 1295 if (site && note) { 1296 on = !ActivityPubService.get TimelineReaction(site.slug, note).boosted;1296 on = !ActivityPubService.getReaction(site.slug, note).boosted; 1297 1297 try { await ActivityPubService.sendInteraction(site, on ? 'boost' : 'unboost', note, (req.body.author || '').toString()); } catch (e) { /* ignore */ } 1298 1298 ActivityPubService.setReaction(site.slug, note, 'boost', on); // instant UI state -
src/services/ActivityPubService.js
r0e27e54 r14f7cb2 3429 3429 * werk voor fase 2, mét datamigratie. 3430 3430 */ 3431 /** 3432 * Wat heb IK met dit object gedaan? Leest de tussentabel, de bron van waarheid 3433 * sinds shaer-9e9 fase 2. Vervangt getMyReactions en getTimelineReaction, die 3434 * dezelfde vraag beantwoordden uit twee verschillende bronnen. 3435 */ 3436 export function getReaction(slug, uri) { 3437 try { 3438 const rows = (slug && uri) 3439 ? db.prepare('SELECT kind FROM ap_my_reactions WHERE site_slug = ? AND target_uri = ?').all(slug, uri) 3440 : []; 3441 return { liked: rows.some((r) => r.kind === 'like'), boosted: rows.some((r) => r.kind === 'boost') }; 3442 } catch { return { liked: false, boosted: false }; } 3443 } 3444 3445 /** 3446 * Dezelfde vraag voor een hele pagina in EEN query. De C2S-tijdlijn zet 3447 * shaer:liked op elke post; per rij vragen zou dat een N+1 maken, en dan had je 3448 * een consistentiebug geruild voor een traagheidsbug. 3449 */ 3450 export function getReactionsFor(slug, uris) { 3451 const out = new Map(); 3452 const list = [...new Set((uris || []).filter(Boolean))].slice(0, 500); 3453 if (!slug || !list.length) return out; 3454 try { 3455 const rows = db.prepare( 3456 `SELECT target_uri, kind FROM ap_my_reactions 3457 WHERE site_slug = ? AND target_uri IN (${list.map(() => '?').join(',')})`, 3458 ).all(slug, ...list); 3459 for (const r of rows) { 3460 const cur = out.get(r.target_uri) || { liked: false, boosted: false }; 3461 if (r.kind === 'like') cur.liked = true; 3462 if (r.kind === 'boost') cur.boosted = true; 3463 out.set(r.target_uri, cur); 3464 } 3465 } catch { /* leeg = niets gereageerd, en dat is een veilige uitkomst */ } 3466 return out; 3467 } 3468 3431 3469 export function setReaction(slug, uri, kind, on, opts = {}) { 3432 3470 if (!slug || !uri || (kind !== 'like' && kind !== 'boost')) return; … … 4809 4847 gateOutgoingFollow, performApprovedFollow, 4810 4848 parseOwnPoll, pollTally, ownPollView, deliverPollUpdate, maybeCrawlThread, sendReport, localMentionSlugs, 4811 autoBoostCount, boostedCount, markBoosted, unmarkBoosted, markLiked, unmarkLiked, setReaction, get TimelineReaction, upsertBoostedNote, getCirkelPosts, getCirkelMembers, selfHealTimeline,4849 autoBoostCount, boostedCount, markBoosted, unmarkBoosted, markLiked, unmarkLiked, setReaction, getReaction, getReactionsFor, getTimelineReaction, upsertBoostedNote, getCirkelPosts, getCirkelMembers, selfHealTimeline, 4812 4850 getNotifications, listBlocks, isBlockedAny, blockTarget, unblock, 4813 4851 deliverWithRetry, enqueueDelivery, processDeliveryQueue, startDeliveryWorker, -
test/reactions-characterization.test.js
r0e27e54 r14f7cb2 212 212 assert.equal(AP.getMyReactions('me', uri('sr5')).liked, false); 213 213 }); 214 215 // ── Fase 2: lezen komt uit de tussentabel ──────────────────────────────── 216 217 test('getReaction leest de tussentabel, niet de kolom', () => { 218 const u = uri('g1'); seedTimeline(u); 219 AP.setReaction('me', u, 'like', true); 220 assert.deepEqual(AP.getReaction('me', u), { liked: true, boosted: false }); 221 AP.setReaction('me', u, 'boost', true); 222 assert.deepEqual(AP.getReaction('me', u), { liked: true, boosted: true }); 223 }); 224 225 test('WAAROM DE BACKFILL EERST MOET: een kale kolomvlag is onzichtbaar voor getReaction', () => { 226 // Dit is de reden dat scripts/backfill-reactions.mjs tussen fase 1 en 2 hoort. 227 // Een reactie van vóór fase 1 staat alleen in de kolom; zodra de lezers de 228 // tussentabel volgen is die stil verdwenen -- geen fout, geen spoor. 229 const u = uri('g2'); seedTimeline(u); 230 AP.markLiked('me', u); // zoals de oude tijdlijn-route 231 assert.equal(AP.getTimelineReaction('me', u).liked, true, 'de kolom staat aan'); 232 assert.equal(AP.getReaction('me', u).liked, false, 'maar het nieuwe leespad ziet hem niet'); 233 // Wat de backfill doet: 234 AP.setMyReaction('me', u, 'like', true); 235 assert.equal(AP.getReaction('me', u).liked, true, 'na aanvullen wel'); 236 }); 237 238 test('getReactionsFor haalt een hele pagina in één keer op', () => { 239 const a = uri('g3'), b = uri('g4'), c = uri('g5'); 240 seedTimeline(a); seedTimeline(b); 241 AP.setReaction('me', a, 'like', true); 242 AP.setReaction('me', b, 'boost', true); 243 const m = AP.getReactionsFor('me', [a, b, c]); 244 assert.equal(m.get(a).liked, true); 245 assert.equal(m.get(b).boosted, true); 246 assert.equal(m.get(c), undefined, 'wie niets heeft komt niet in de map; de aanroeper valt terug op false'); 247 // Dezelfde uitkomst als per stuk vragen, zodat de batch geen eigen waarheid wordt. 248 for (const u of [a, b]) assert.deepEqual(m.get(u), AP.getReaction('me', u)); 249 }); 250 251 test('getReactionsFor: lege of rommelige invoer geeft een lege map', () => { 252 assert.equal(AP.getReactionsFor('me', []).size, 0); 253 assert.equal(AP.getReactionsFor('me', null).size, 0); 254 assert.equal(AP.getReactionsFor('', [uri('g3')]).size, 0); 255 });
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)