Changeset 3d37c67 in Klonkt
- Timestamp:
- 06/26/2026 11:38:25 PM (2 months ago)
- Branches:
- main
- Children:
- 2f42b60
- Parents:
- 3289a64
- Location:
- src
- Files:
-
- 4 edited
-
config/database.js (modified) (1 diff)
-
routes/posts.js (modified) (1 diff)
-
services/ActivityPubService.js (modified) (2 diffs)
-
views/pages/authorize-interaction.ejs (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/config/database.js
r3289a64 r3d37c67 354 354 ); 355 355 CREATE INDEX IF NOT EXISTS idx_ap_outbox_post ON ap_outbox(post_id); 356 -- Your like/boost state on a REMOTE post (the interact page), so those become toggles. 357 CREATE TABLE IF NOT EXISTS ap_my_reactions ( 358 site_slug TEXT NOT NULL, 359 target_uri TEXT NOT NULL, 360 kind TEXT NOT NULL, -- 'like' | 'boost' 361 created_at DATETIME DEFAULT CURRENT_TIMESTAMP, 362 UNIQUE(site_slug, target_uri, kind) 363 ); 356 364 `); 357 365 ensureColumn('ap_interactions', 'parent_uri', 'TEXT'); // nesting (existing DBs) -
src/routes/posts.js
r3289a64 r3d37c67 502 502 liked: !!req.query.liked, 503 503 boosted: !!req.query.boosted, 504 reacted: (site && uri) ? ActivityPubService.getMyReactions(site.slug, uri) : { liked: false, boosted: false }, 504 505 siteTitle: site ? site.title : '', 505 506 }); 506 507 }); 507 508 508 // ⭐ Like a remote post from your own site (the star flow lands here).509 // ⭐ Like / unlike a remote post from your own site (toggle on the interact page). 509 510 router.post('/authorize_interaction/like', requireSiteManager, (req, res) => { 510 511 const site = res.locals.site; 511 512 const uri = (req.body.uri || '').toString(); 512 513 if (site && uri) { 514 const on = !ActivityPubService.getMyReactions(site.slug, uri).liked; 513 515 ActivityPubService.resolveRemoteNote(uri) 514 .then((note) => note && ActivityPubService.sendInteraction(site, 'like', note.object_uri || uri, note.actor_uri))516 .then((note) => note && ActivityPubService.sendInteraction(site, on ? 'like' : 'unlike', note.object_uri || uri, note.actor_uri)) 515 517 .catch((e) => console.warn('[AP] remote like failed:', e.message)); 516 } 517 res.redirect('/authorize_interaction?liked=1&uri=' + encodeURIComponent(uri)); 518 }); 519 520 // 🔁 Boost a remote post from your own site. Also flags it for the Cirkel 521 // (markBoosted is a no-op if the post isn't in your timeline). 518 ActivityPubService.setMyReaction(site.slug, uri, 'like', on); 519 } 520 res.redirect('/authorize_interaction?uri=' + encodeURIComponent(uri)); 521 }); 522 523 // 🔁 Boost / unboost a remote post from your own site (toggle on the interact page). 524 // Also flags it for the Cirkel (markBoosted is a no-op if the post isn't in your timeline). 522 525 router.post('/authorize_interaction/boost', requireSiteManager, (req, res) => { 523 526 const site = res.locals.site; 524 527 const uri = (req.body.uri || '').toString(); 525 528 if (site && uri) { 529 const on = !ActivityPubService.getMyReactions(site.slug, uri).boosted; 526 530 ActivityPubService.resolveRemoteNote(uri) 527 531 .then((note) => { 528 532 if (!note) return; 529 533 const id = note.object_uri || uri; 530 return Promise.resolve(ActivityPubService.sendInteraction(site, 'boost', id, note.actor_uri))531 .then(() => ActivityPubService.markBoosted(site.slug, id));534 return Promise.resolve(ActivityPubService.sendInteraction(site, on ? 'boost' : 'unboost', id, note.actor_uri)) 535 .then(() => on ? ActivityPubService.markBoosted(site.slug, id) : ActivityPubService.unmarkBoosted(site.slug, id)); 532 536 }) 533 537 .catch((e) => console.warn('[AP] remote boost failed:', e.message)); 534 } 535 res.redirect('/authorize_interaction?boosted=1&uri=' + encodeURIComponent(uri)); 538 ActivityPubService.setMyReaction(site.slug, uri, 'boost', on); 539 } 540 res.redirect('/authorize_interaction?uri=' + encodeURIComponent(uri)); 536 541 }); 537 542 -
src/services/ActivityPubService.js
r3289a64 r3d37c67 367 367 export function setInteractionLiked(id, on) { 368 368 db.prepare('UPDATE ap_interactions SET acted_like = ? WHERE id = ?').run(on ? 1 : 0, id); 369 } 370 // Your like/boost state on a REMOTE post (interact page toggles). 371 export function setMyReaction(slug, uri, kind, on) { 372 if (on) db.prepare('INSERT OR IGNORE INTO ap_my_reactions (site_slug, target_uri, kind) VALUES (?,?,?)').run(slug, uri, kind); 373 else db.prepare('DELETE FROM ap_my_reactions WHERE site_slug = ? AND target_uri = ? AND kind = ?').run(slug, uri, kind); 374 } 375 export function getMyReactions(slug, uri) { 376 const rows = (slug && uri) ? db.prepare('SELECT kind FROM ap_my_reactions WHERE site_slug = ? AND target_uri = ?').all(slug, uri) : []; 377 return { liked: rows.some((r) => r.kind === 'like'), boosted: rows.some((r) => r.kind === 'boost') }; 369 378 } 370 379 … … 1337 1346 buildActor, buildNote, buildCreate, buildOutbox, buildFollowers, buildFeatured, 1338 1347 followerCount, deliver, fetchActor, verifyRequest, handleInbox, deliverCreate, deliverDelete, deliverUpdate, deliverActorUpdate, resyncFeaturedPins, 1339 getInteractions, getInteractionById, setInteractionBoosted, setInteractionLiked, buildReplyNote, getOutboxNote, deliverReply, resolveRemoteNote,1348 getInteractions, getInteractionById, setInteractionBoosted, setInteractionLiked, setMyReaction, getMyReactions, buildReplyNote, getOutboxNote, deliverReply, resolveRemoteNote, 1340 1349 listOutbox, deliverOutboxDelete, 1341 1350 webfingerResolve, followActor, resolveRemoteActor, unfollowActor, listFollowing, setAutoBoost, getTimeline, sendInteraction, -
src/views/pages/authorize-interaction.ejs
r3289a64 r3d37c67 95 95 <form method="post" action="/authorize_interaction/like"> 96 96 <input type="hidden" name="uri" value="<%= uri %>"> 97 <button type="submit" class="btn btn-primary auth-like-btn">★ <%= t('fedi.like_btn') %></button> 97 <button type="submit" class="fedi-bigact fedi-bigact-like<%= (typeof reacted !== 'undefined' && reacted.liked) ? ' is-on' : '' %>"> 98 <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.9 5.88 6.49.95-4.7 4.58 1.11 6.46L12 17.96l-5.8 3.06 1.1-6.46-4.69-4.58 6.49-.95z"/></svg> 99 <span><%= (typeof reacted !== 'undefined' && reacted.liked) ? t('fedi.unlike_short') : t('fedi.like_short') %></span> 100 </button> 98 101 </form> 99 102 <form method="post" action="/authorize_interaction/boost"> 100 103 <input type="hidden" name="uri" value="<%= uri %>"> 101 <button type="submit" class="btn auth-boost-btn">🔁 <%= t('fedi.boost_btn') %></button> 104 <button type="submit" class="fedi-bigact fedi-bigact-boost<%= (typeof reacted !== 'undefined' && reacted.boosted) ? ' is-on' : '' %>"> 105 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="17 1 21 5 17 9"/><path d="M3 11V9a4 4 0 0 1 4-4h14"/><polyline points="7 23 3 19 7 15"/><path d="M21 13v2a4 4 0 0 1-4 4H3"/></svg> 106 <span><%= (typeof reacted !== 'undefined' && reacted.boosted) ? t('tl.unboost') : t('fedi.boost_short') %></span> 107 </button> 102 108 </form> 103 109 </div> … … 146 152 text-decoration: none; font-weight: 600; font-size: .92rem; transition: background .15s, border-color .15s; } 147 153 .auth-view-src:hover { background: color-mix(in srgb, var(--accent, #888) 18%, transparent); border-color: var(--accent, #888); } 148 .auth-interact-react { display: flex; gap: . 5rem; flex-wrap: wrap; margin: 0 0 .25rem; }154 .auth-interact-react { display: flex; gap: .6rem; flex-wrap: wrap; margin: 0 0 .25rem; } 149 155 .auth-interact-react form { margin: 0; } 150 .auth-boost-btn { white-space: nowrap; } 156 .fedi-bigact { display: inline-flex; align-items: center; gap: .5rem; padding: .55rem 1.1rem; border-radius: 999px; 157 border: 1px solid color-mix(in srgb, var(--ink, #000) 14%, transparent); 158 background: color-mix(in srgb, var(--ink, #000) 4%, transparent); 159 color: var(--ink, inherit); font: inherit; font-weight: 600; font-size: .92rem; cursor: pointer; white-space: nowrap; 160 transition: background .12s, border-color .12s; } 161 .fedi-bigact svg { width: 17px; height: 17px; flex: 0 0 17px; } 162 .fedi-bigact-like svg { color: #e8b04b; } 163 .fedi-bigact-like:hover, .fedi-bigact-like.is-on { background: color-mix(in srgb, #e8b04b 16%, transparent); border-color: color-mix(in srgb, #e8b04b 55%, transparent); } 164 .fedi-bigact-boost svg { color: #2fa85a; } 165 .fedi-bigact-boost:hover, .fedi-bigact-boost.is-on { background: color-mix(in srgb, #2fa85a 16%, transparent); border-color: color-mix(in srgb, #2fa85a 55%, transparent); } 151 166 .fedi-manage { list-style: none; padding: 0; margin: 1.25rem 0 1rem; display: flex; flex-direction: column; gap: .7rem; } 152 167 .fedi-manage-item { padding: .85rem 1rem; border-radius: 14px; background: color-mix(in srgb, var(--ink, #000) 4%, transparent); border: 1px solid color-mix(in srgb, var(--ink, #000) 9%, transparent); }
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)