- Timestamp:
- 08/06/2026 08:46:01 AM (5 weeks ago)
- Branches:
- main
- Children:
- 0e27e54
- Parents:
- 02fb7ce
- git-author:
- Robin <roboburr@…> (08/06/2026 08:46:00 AM)
- git-committer:
- Claude (agent) <aiclaude@…> (08/06/2026 08:46:01 AM)
- Location:
- src
- Files:
-
- 2 edited
-
routes/posts.js (modified) (4 diffs)
-
services/ActivityPubService.js (modified) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/routes/posts.js
r02fb7ce rc010b42 843 843 .then((note) => note && ActivityPubService.sendInteraction(site, on ? 'like' : 'unlike', note.object_uri || uri, note.actor_uri)) 844 844 .catch((e) => console.warn('[AP] remote like failed:', e.message)); 845 ActivityPubService.setMyReaction(site.slug, uri, 'like', on); 845 // Eén schrijfpad (shaer-9e9): tussentabel + afgeleide vlag. 846 ActivityPubService.setReaction(site.slug, uri, 'like', on); 846 847 } 847 848 if (req.get('X-Requested-With') === 'fetch') return res.json({ ok: true, on }); … … 862 863 const id = note.object_uri || uri; 863 864 return Promise.resolve(ActivityPubService.sendInteraction(site, on ? 'boost' : 'unboost', id, note.actor_uri)) 864 // Boost → store the post in the timeline (even if you don't follow the author) so it 865 // surfaces in the Cirkel; unboost → just clear the flag. 866 .then(() => on ? ActivityPubService.upsertBoostedNote(site.slug, note) : ActivityPubService.unmarkBoosted(site.slug, id)); 865 // De note gaat mee: een boost zet niet alleen een vlag maar trekt de 866 // post je tijdlijn in, ook als je de auteur niet volgt, zodat hij in 867 // de Cirkel verschijnt. 868 .then(() => ActivityPubService.setReaction(site.slug, uri, 'boost', on, { flagUri: id, note: on ? note : null })); 867 869 }) 868 870 .catch((e) => console.warn('[AP] remote boost failed:', e.message)); 871 // De tussentabel meteen, zodat de knop klopt voordat de resolve terug is. 869 872 ActivityPubService.setMyReaction(site.slug, uri, 'boost', on); 870 873 } … … 1279 1282 on = !ActivityPubService.getTimelineReaction(site.slug, note).liked; 1280 1283 try { await ActivityPubService.sendInteraction(site, on ? 'like' : 'unlike', note, (req.body.author || '').toString()); } catch (e) { /* ignore */ } 1281 if (on) ActivityPubService.markLiked(site.slug, note); else ActivityPubService.unmarkLiked(site.slug, note);1284 ActivityPubService.setReaction(site.slug, note, 'like', on); 1282 1285 } 1283 1286 if (req.get('X-Requested-With') === 'fetch') return res.json({ ok: true, on }); … … 1293 1296 on = !ActivityPubService.getTimelineReaction(site.slug, note).boosted; 1294 1297 try { await ActivityPubService.sendInteraction(site, on ? 'boost' : 'unboost', note, (req.body.author || '').toString()); } catch (e) { /* ignore */ } 1298 ActivityPubService.setReaction(site.slug, note, 'boost', on); // instant UI state 1295 1299 if (on) { 1296 ActivityPubService.markBoosted(site.slug, note); // instant UI state1297 1300 // Fire-and-forget: re-resolve the note so the cached row is refreshed 1298 1301 // (cover/content) — boosting again heals a stale copy from EVERY boost 1299 1302 // path, not just the interact page. 1300 1303 ActivityPubService.resolveRemoteNote(note) 1301 .then((n) => { if (n) ActivityPubService. upsertBoostedNote(site.slug, n); })1304 .then((n) => { if (n) ActivityPubService.setReaction(site.slug, note, 'boost', true, { note: n }); }) 1302 1305 .catch(() => { /* best-effort */ }); 1303 } else {1304 ActivityPubService.unmarkBoosted(site.slug, note);1305 1306 } 1306 1307 } -
src/services/ActivityPubService.js
r02fb7ce rc010b42 2623 2623 const kind = type === 'Announce' ? 'boost' : 'like'; 2624 2624 await sendInteraction(site, kind, objUri, authorUri); 2625 setMyReaction(site.slug, targetUri, kind, true); 2626 if (type === 'Announce' && note) { try { upsertBoostedNote(site.slug, note); } catch { /* non-fatal */ } } 2625 // Eén schrijfpad (shaer-9e9): tussentabel + afgeleide vlag in één keer. 2626 // De note gaat mee zodat een boost de post je tijdlijn in trekt. 2627 try { setReaction(site.slug, targetUri, kind, true, { flagUri: objUri, note: type === 'Announce' ? note : null }); } 2628 catch { /* non-fatal: een reactie mag nooit de bezorging blokkeren */ } 2627 2629 // Een Like uit een app moet ook in ap_timeline.liked landen, want dat 2628 2630 // is wat de C2S-tijdlijn als shaer:liked teruggeeft. Zonder dit werd … … 2634 2636 // in je tijdlijn te trekken, dus staat de post er niet in, dan is dit 2635 2637 // terecht een no-op. 2636 if (type === 'Like') { try { markLiked(site.slug, objUri); } catch { /* non-fatal */ } }2637 2638 return { status: 202, url: objUri }; 2638 2639 } … … 2683 2684 const objUri = (note && note.object_uri) || innerTarget; 2684 2685 await sendInteraction(site, kind, objUri, note && note.actor_uri); 2685 setMyReaction(site.slug, innerTarget, innerType === 'Announce' ? 'boost' : 'like', false); 2686 if (innerType === 'Announce') { try { unmarkBoosted(site.slug, objUri); } catch { /* non-fatal */ } } 2687 if (innerType === 'Like') { try { unmarkLiked(site.slug, objUri); } catch { /* non-fatal */ } } 2686 try { setReaction(site.slug, innerTarget, innerType === 'Announce' ? 'boost' : 'like', false, { flagUri: objUri }); } 2687 catch { /* non-fatal */ } 2688 2688 return { status: 202, url: objUri }; 2689 2689 } … … 3411 3411 try { if (!_unmarkLike) _unmarkLike = db.prepare('UPDATE ap_timeline SET liked = 0 WHERE slug = ? AND id = ?'); _unmarkLike.run(slug, noteId); } catch { /* ignore */ } 3412 3412 } 3413 /** 3414 * Zet een reactie van JOU op een object. Dit hoort het enige schrijfpad te zijn 3415 * (shaer-9e9): de tussentabel ap_my_reactions is de waarheid, de vlaggen op 3416 * ap_timeline zijn de afgeleide. Zolang markLiked en broers los aanroepbaar 3417 * blijven kan een aanroeper ze vergeten, en dat is niet hypothetisch -- precies 3418 * dat leverde de shaer:liked-bug op (04aca12). 3419 * 3420 * `opts.note` is de opgeloste remote note bij een boost. Die is niet optioneel 3421 * uit netheid: een boost moet de post je tijdlijn IN trekken als je de auteur 3422 * niet volgt, anders heeft de vlag geen rij om op te landen en verschijnt de 3423 * boost nergens -- ook niet in de Cirkel. 3424 * 3425 * `opts.flagUri` bestaat omdat de twee bronnen vandaag verschillend gesleuteld 3426 * worden: de tussentabel op de URI die de client stuurde, de vlag op de 3427 * opgeloste object-URI. Meestal zijn die gelijk, maar niet gegarandeerd. Deze 3428 * naad houdt fase 1 gedragsbehoudend; het samentrekken van die twee sleutels is 3429 * werk voor fase 2, mét datamigratie. 3430 */ 3431 export function setReaction(slug, uri, kind, on, opts = {}) { 3432 if (!slug || !uri || (kind !== 'like' && kind !== 'boost')) return; 3433 setMyReaction(slug, uri, kind, !!on); 3434 const flagUri = opts.flagUri || uri; 3435 if (kind === 'boost') { 3436 if (!on) unmarkBoosted(slug, flagUri); 3437 else if (opts.note) upsertBoostedNote(slug, opts.note); 3438 else markBoosted(slug, flagUri); 3439 } else if (on) markLiked(slug, flagUri); 3440 else unmarkLiked(slug, flagUri); 3441 } 3442 3413 3443 export function getTimelineReaction(slug, noteId) { 3414 3444 try { const r = db.prepare('SELECT liked, boosted FROM ap_timeline WHERE slug = ? AND id = ?').get(slug, noteId); return { liked: !!(r && r.liked), boosted: !!(r && r.boosted) }; } catch { return { liked: false, boosted: false }; } … … 4779 4809 gateOutgoingFollow, performApprovedFollow, 4780 4810 parseOwnPoll, pollTally, ownPollView, deliverPollUpdate, maybeCrawlThread, sendReport, localMentionSlugs, 4781 autoBoostCount, boostedCount, markBoosted, unmarkBoosted, markLiked, unmarkLiked, getTimelineReaction, upsertBoostedNote, getCirkelPosts, getCirkelMembers, selfHealTimeline,4811 autoBoostCount, boostedCount, markBoosted, unmarkBoosted, markLiked, unmarkLiked, setReaction, getTimelineReaction, upsertBoostedNote, getCirkelPosts, getCirkelMembers, selfHealTimeline, 4782 4812 getNotifications, listBlocks, isBlockedAny, blockTarget, unblock, 4783 4813 deliverWithRetry, enqueueDelivery, processDeliveryQueue, startDeliveryWorker,
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)