Changeset 74d61e6 in Klonkt


Ignore:
Timestamp:
06/27/2026 07:15:25 AM (2 months ago)
Author:
Robin Genis <roboburr@…>
Branches:
main
Children:
f99c830
Parents:
dcff893
Message:

feat(fedi): boosting a non-followed post shows it in the Cirkel

The interact-page boost now stores the remote post in ap_timeline (INSERT OR IGNORE, no
dup for followed posts) before flagging it boosted, so a boost of someone you don't
follow still surfaces in the Cirkel with a Boost badge. upsertBoostedNote(slug, note).

Location:
src
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • src/routes/posts.js

    rdcff893 r74d61e6  
    537537        const id = note.object_uri || uri;
    538538        return Promise.resolve(ActivityPubService.sendInteraction(site, on ? 'boost' : 'unboost', id, note.actor_uri))
    539           .then(() => on ? ActivityPubService.markBoosted(site.slug, id) : ActivityPubService.unmarkBoosted(site.slug, id));
     539          // Boost → store the post in the timeline (even if you don't follow the author) so it
     540          // surfaces in the Cirkel; unboost → just clear the flag.
     541          .then(() => on ? ActivityPubService.upsertBoostedNote(site.slug, note) : ActivityPubService.unmarkBoosted(site.slug, id));
    540542      })
    541543      .catch((e) => console.warn('[AP] remote boost failed:', e.message));
  • src/services/ActivityPubService.js

    rdcff893 r74d61e6  
    11281128  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 }; }
    11291129}
     1130// Boost a REMOTE post that may not be in your timeline (you don't follow the author):
     1131// store it in ap_timeline (INSERT OR IGNORE → no dup for followed posts) so it shows in
     1132// the Cirkel with a Boost badge, then flag it boosted.
     1133export function upsertBoostedNote(slug, note) {
     1134  if (!slug || !note || !note.object_uri) return;
     1135  const id = note.object_uri;
     1136  const media = JSON.stringify((note.images || []).map((u) => ({ url: u, type: 'image/jpeg' })));
     1137  try {
     1138    tlStmts().ins.run(id, slug, note.actor_uri || '', note.actor_name || '', note.actor_handle || '',
     1139      note.actor_icon || '', note.actor_url || '', note.content || '', note.url || null,
     1140      new Date().toISOString(), media);
     1141  } catch { /* ignore */ }
     1142  markBoosted(slug, id);
     1143}
    11301144export function boostedCount(slug) {
    11311145  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; }
     
    13781392  listOutbox, deliverOutboxDelete,
    13791393  webfingerResolve, followActor, resolveRemoteActor, unfollowActor, listFollowing, setAutoBoost, getTimeline, sendInteraction,
    1380   autoBoostCount, boostedCount, markBoosted, unmarkBoosted, markLiked, unmarkLiked, getTimelineReaction, getCirkelPosts, getCirkelMembers, selfHealTimeline,
     1394  autoBoostCount, boostedCount, markBoosted, unmarkBoosted, markLiked, unmarkLiked, getTimelineReaction, upsertBoostedNote, getCirkelPosts, getCirkelMembers, selfHealTimeline,
    13811395  getNotifications, listBlocks, isBlockedAny, blockTarget, unblock,
    13821396  deliverWithRetry, enqueueDelivery, processDeliveryQueue, startDeliveryWorker,
Note: See TracChangeset for help on using the changeset viewer.