Index: src/routes/activitypub.js
===================================================================
--- src/routes/activitypub.js	(revision c010b423494784ff556a97a64ba69c3c733ffe0e)
+++ src/routes/activitypub.js	(revision 14f7cb2a1b5add6334373b9865b7bfb7e4e2bc66)
@@ -285,5 +285,10 @@
   const playbackAllowed = embedsAllowed
     && Guardianship.externalPlaybackAllowed(auth.site.external_playback, isWard);
-  const posts = AP.getTimeline(auth.site.slug, 60).map((t) => ({
+  const rows = AP.getTimeline(auth.site.slug, 60);
+  // Eén query voor de hele pagina (shaer-9e9 fase 2): shaer:liked komt uit de
+  // tussentabel, de bron van waarheid, en niet meer uit de afgeleide kolom op
+  // ap_timeline. Per rij vragen zou hier een N+1 opleveren.
+  const reacties = AP.getReactionsFor(auth.site.slug, rows.map((t) => t.id));
+  const posts = rows.map((t) => ({
     id: `${t.id}#create`,
     type: 'Create',
@@ -333,6 +338,6 @@
       // Whether THIS account already liked/boosted the note, so the app's
       // detail-view buttons show the current state (and can toggle/undo).
-      'shaer:liked': !!t.liked,
-      'shaer:boosted': !!t.boosted,
+      'shaer:liked': !!(reacties.get(t.id) || {}).liked,
+      'shaer:boosted': !!(reacties.get(t.id) || {}).boosted,
       // An external (non-fediverse) embed, thumbnail-only and never an iframe.
       // Omitted entirely when the gate is closed (see above).
Index: src/routes/posts.js
===================================================================
--- src/routes/posts.js	(revision c010b423494784ff556a97a64ba69c3c733ffe0e)
+++ src/routes/posts.js	(revision 14f7cb2a1b5add6334373b9865b7bfb7e4e2bc66)
@@ -806,5 +806,5 @@
     liked: !!req.query.liked,
     boosted: !!req.query.boosted,
-    reacted: (site && uri) ? ActivityPubService.getMyReactions(site.slug, uri) : { liked: false, boosted: false },
+    reacted: (site && uri) ? ActivityPubService.getReaction(site.slug, uri) : { liked: false, boosted: false },
     siteTitle: site ? site.title : '',
   });
@@ -839,5 +839,5 @@
   let on = false;
   if (site && uri) {
-    on = !ActivityPubService.getMyReactions(site.slug, uri).liked;
+    on = !ActivityPubService.getReaction(site.slug, uri).liked;
     ActivityPubService.resolveRemoteNote(uri)
       .then((note) => note && ActivityPubService.sendInteraction(site, on ? 'like' : 'unlike', note.object_uri || uri, note.actor_uri))
@@ -857,5 +857,5 @@
   let on = false;
   if (site && uri) {
-    on = !ActivityPubService.getMyReactions(site.slug, uri).boosted;
+    on = !ActivityPubService.getReaction(site.slug, uri).boosted;
     ActivityPubService.resolveRemoteNote(uri)
       .then((note) => {
@@ -1280,5 +1280,5 @@
   let on = false;
   if (site && note) {
-    on = !ActivityPubService.getTimelineReaction(site.slug, note).liked;
+    on = !ActivityPubService.getReaction(site.slug, note).liked;
     try { await ActivityPubService.sendInteraction(site, on ? 'like' : 'unlike', note, (req.body.author || '').toString()); } catch (e) { /* ignore */ }
     ActivityPubService.setReaction(site.slug, note, 'like', on);
@@ -1294,5 +1294,5 @@
   let on = false;
   if (site && note) {
-    on = !ActivityPubService.getTimelineReaction(site.slug, note).boosted;
+    on = !ActivityPubService.getReaction(site.slug, note).boosted;
     try { await ActivityPubService.sendInteraction(site, on ? 'boost' : 'unboost', note, (req.body.author || '').toString()); } catch (e) { /* ignore */ }
     ActivityPubService.setReaction(site.slug, note, 'boost', on); // instant UI state
