Changeset 25f2e64 in Klonkt
- Timestamp:
- 08/06/2026 09:46:01 AM (5 weeks ago)
- Branches:
- main
- Children:
- 5296349
- Parents:
- 7842ca1
- git-author:
- Robin <roboburr@…> (08/06/2026 09:46:00 AM)
- git-committer:
- Claude (agent) <aiclaude@…> (08/06/2026 09:46:01 AM)
- Files:
-
- 2 edited
-
src/services/ActivityPubService.js (modified) (3 diffs)
-
test/reactions-characterization.test.js (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/services/ActivityPubService.js
r7842ca1 r25f2e64 3192 3192 return { ins: _insTl, list: _listTl, del: _delTl }; 3193 3193 } 3194 export function getTimeline(slug, limit, offset) { return tlStmts().list.all(slug, limit || 50, offset || 0); } 3194 /** 3195 * De tijdlijn, met liked/boosted uit de TUSSENTABEL (shaer-9e9). 3196 * 3197 * De rijen komen met SELECT *, dus ap_timeline.liked en .boosted liften mee -- 3198 * en die zijn sinds fase 1 nog maar een afgeleide. De Krant tekende zijn 3199 * knoppen daar wel op, terwijl de toggle al uit getReaction besliste: tekenen en 3200 * beslissen leunden dus op verschillende bronnen. Ze waren het eens zolang de 3201 * migratie ze gelijk hield, maar dat was synchronisatie en geen ontwerp. 3202 * 3203 * Bewust in JS en niet als join: met SELECT * zouden twee kolommen `liked` 3204 * heten en hangt het van de driver af welke wint. Eén extra query per pagina 3205 * (dezelfde batch die de C2S-tijdlijn gebruikt) is dat niet waard. 3206 */ 3207 export function getTimeline(slug, limit, offset) { 3208 const rows = tlStmts().list.all(slug, limit || 50, offset || 0); 3209 const reacties = getReactionsFor(slug, rows.map((r) => r.id)); 3210 for (const r of rows) { 3211 const x = reacties.get(r.id); 3212 r.liked = !!(x && x.liked); 3213 r.boosted = !!(x && x.boosted); 3214 } 3215 return rows; 3216 } 3195 3217 3196 3218 /** … … 3384 3406 if (!_cirkelPosts) _cirkelPosts = db.prepare(` 3385 3407 SELECT t.id, t.author_uri, t.author_name, t.author_handle, t.author_icon, t.author_url, 3386 t.content, t.url, t.published, t.media_json, t.boosted, t.nsfw, t.cw 3408 t.content, t.url, t.published, t.media_json, t.nsfw, t.cw, 3409 (rb.target_uri IS NOT NULL) AS boosted 3387 3410 FROM ap_timeline t 3388 3411 LEFT JOIN ap_following f ON f.slug = t.slug AND f.actor_uri = t.author_uri 3389 WHERE t.slug = ? AND (f.auto_boost = 1 OR t.boosted = 1) 3412 -- Uit de tussentabel, niet uit t.boosted: die kolom is een afgeleide. De 3413 -- UNIQUE(site_slug, target_uri, kind) garandeert hoogstens één match, dus 3414 -- deze join kan geen rijen verdubbelen. 3415 LEFT JOIN ap_my_reactions rb ON rb.site_slug = t.slug AND rb.target_uri = t.id AND rb.kind = 'boost' 3416 WHERE t.slug = ? AND (f.auto_boost = 1 OR rb.target_uri IS NOT NULL) 3390 3417 ORDER BY COALESCE(t.published, t.created_at) DESC, t.rowid DESC 3391 3418 LIMIT ? OFFSET ?`); … … 3637 3664 } 3638 3665 export function boostedCount(slug) { 3639 try { if (!_boostedCount) _boostedCount = db.prepare('SELECT COUNT(*) AS n FROM ap_timeline WHERE slug = ? AND boosted = 1'); return _boostedCount.get(slug).n; } catch { return 0; } 3666 // Geboost EN in je tijdlijn, zoals voorheen: de tussentabel kan ook een boost 3667 // bevatten van iets dat er (nog) niet in staat. 3668 try { 3669 if (!_boostedCount) _boostedCount = db.prepare(`SELECT COUNT(*) AS n FROM ap_my_reactions r 3670 JOIN ap_timeline t ON t.slug = r.site_slug AND t.id = r.target_uri 3671 WHERE r.site_slug = ? AND r.kind = 'boost'`); 3672 return _boostedCount.get(slug).n; 3673 } catch { return 0; } 3640 3674 } 3641 3675 -
test/reactions-characterization.test.js
r7842ca1 r25f2e64 61 61 }); 62 62 63 test('tijdlijn-route: markBoosted zet de kolom en de post verschijnt in de Cirkel', () => { 63 test('tijdlijn-route: markBoosted zet de kolom, maar de Cirkel volgt de tussentabel', () => { 64 // BIJGEWERKT toen de weergave werd gelijkgetrokken. Vroeger trok een kale 65 // kolomvlag de post de Cirkel in; nu selecteert de Cirkel op de tussentabel, 66 // dus de primitief alleen is niet meer genoeg. Dat is het doel: tekenen en 67 // beslissen leunen op dezelfde bron. 64 68 const u = uri('t2'); seedTimeline(u); 65 assert.equal(AP.boostedCount('me'), 0);66 69 AP.markBoosted('me', u); 67 assert.equal(AP.getTimelineReaction('me', u).boosted, true); 68 assert.equal(AP.boostedCount('me'), 1); 69 // De Cirkel is wat een gebruiker ziet: geboost = zichtbaar daar. 70 assert.ok(AP.getCirkelPosts('me', 60, 0).some((p) => p.id === u), 'geboost hoort in de Cirkel te staan'); 71 AP.unmarkBoosted('me', u); 70 assert.equal(AP.getTimelineReaction('me', u).boosted, true, 'de kolom staat aan'); 71 assert.equal(AP.getCirkelPosts('me', 60, 0).some((p) => p.id === u), false, 72 'maar dat brengt hem niet in de Cirkel'); 73 // Via het echte schrijfpad wel. 74 AP.setReaction('me', u, 'boost', true); 75 assert.ok(AP.getCirkelPosts('me', 60, 0).some((p) => p.id === u), 'via setReaction wel'); 76 AP.setReaction('me', u, 'boost', false); 72 77 assert.equal(AP.getCirkelPosts('me', 60, 0).some((p) => p.id === u), false); 73 78 }); … … 117 122 assert.equal(db.prepare('SELECT COUNT(*) AS n FROM ap_timeline WHERE id=?').get(u).n, 1, 'de rij hoort aangemaakt te worden'); 118 123 assert.equal(AP.getTimelineReaction('me', u).boosted, true, 'en meteen als geboost gemarkeerd'); 119 assert.ok(AP.getCirkelPosts('me', 60, 0).some((p) => p.id === u), 'en dus zichtbaar in de Cirkel'); 124 // BIJGEWERKT: de Cirkel volgt sinds het gelijktrekken van de weergave de 125 // tussentabel, en die vult alleen setReaction. upsertBoostedNote blijft 126 // verantwoordelijk voor de RIJ -- zonder die insert heeft de reactie nergens 127 // een post om bij te horen. 128 assert.equal(AP.getCirkelPosts('me', 60, 0).some((p) => p.id === u), false, 'de rij alleen is niet genoeg'); 129 AP.setMyReaction('me', u, 'boost', true); 130 assert.ok(AP.getCirkelPosts('me', 60, 0).some((p) => p.id === u), 'met de tussentabel erbij wel'); 120 131 }); 121 132 … … 363 374 assert.equal(AP.getReaction('me', u).liked, false, 'maar heeft niets geschreven'); 364 375 }); 376 377 // ── De weergave leest dezelfde bron als de knop ────────────────────────── 378 379 test('getTimeline tekent liked/boosted uit de tussentabel, niet uit de kolom', () => { 380 // De Krant tekende zijn knoppen op ap_timeline.liked/boosted terwijl de 381 // toggle al uit getReaction besliste. Tekenen en beslissen leunden dus op 382 // verschillende bronnen -- ze waren het alleen eens zolang iets ze gelijk 383 // hield. Hier wordt de kolom expres tegengesteld gezet. 384 const u = uri('tl1'); seedTimeline(u); 385 AP.setReaction('me', u, 'like', true); 386 db.prepare('UPDATE ap_timeline SET liked = 0, boosted = 1 WHERE slug = ? AND id = ?').run('me', u); 387 388 const rij = AP.getTimeline('me', 50, 0).find((r) => r.id === u); 389 assert.equal(rij.liked, true, 'de tussentabel zegt geliket, dus de knop hoort aan'); 390 assert.equal(rij.boosted, false, 'en de kolom die iets anders beweert telt niet'); 391 }); 392 393 test('de Cirkel selecteert op de tussentabel', () => { 394 const u = uri('tl2'); seedTimeline(u); 395 // Kolom aan, tussentabel leeg: hoort NIET in de Cirkel te staan. 396 db.prepare('UPDATE ap_timeline SET boosted = 1 WHERE slug = ? AND id = ?').run('me', u); 397 assert.equal(AP.getCirkelPosts('me', 60, 0).some((p) => p.id === u), false, 398 'een kale kolomvlag trekt niets meer de Cirkel in'); 399 // En andersom: tussentabel aan, kolom uit. 400 db.prepare('UPDATE ap_timeline SET boosted = 0 WHERE slug = ? AND id = ?').run('me', u); 401 AP.setMyReaction('me', u, 'boost', true); 402 const post = AP.getCirkelPosts('me', 60, 0).find((p) => p.id === u); 403 assert.ok(post, 'de tussentabel bepaalt wat er in de Cirkel staat'); 404 assert.ok(post.boosted, 'en het veld dat de view tekent komt daar ook vandaan'); 405 }); 406 407 test('boostedCount telt de tussentabel, en alleen wat in je tijdlijn staat', () => { 408 const inTl = uri('tl3'), buiten = 'https://elders.test/notes/qqq'; 409 seedTimeline(inTl); 410 const voor = AP.boostedCount('me'); 411 AP.setMyReaction('me', inTl, 'boost', true); 412 AP.setMyReaction('me', buiten, 'boost', true); 413 assert.equal(AP.boostedCount('me'), voor + 1, 'alleen de post die in je tijdlijn staat telt mee'); 414 });
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)