Changeset 6053c6c in Klonkt for src/routes/posts.js


Ignore:
Timestamp:
07/01/2026 07:38:33 AM (2 months ago)
Author:
Robin Genis <roboburr@…>
Branches:
main
Children:
55a73f0
Parents:
f70e010
Message:

feat(fediverse): show + vote on remote polls (Question) in the News feed

Inbound ActivityPub polls, phase 1. A Question (the Mastodon-standard poll)
from a followed account is now cached and rendered in the News feed with its
options, vote counts and close state, and the owner can vote — a ballot is a
Create(Note) carrying only the chosen option's name + inReplyTo the Question,
addressed to the poll's author (the fediverse-standard vote). The author's
Update(Question) refreshes the authoritative counts; our own vote is preserved.

  • config/database.js — ap_timeline.poll_json column
  • services/ActivityPubService.js — parsePoll(); handle Create/Update(Question); voteOnPoll() sends the ballot + optimistic local tally
  • routes/posts.js — parse poll_json for the feed; POST /news/vote (owner-only)
  • views/pages/news.ejs — poll UI (votable options / result bars) + styles
  • services/i18n.js — poll.vote/votes/closed (nl/en/de)

Co-Authored-By: Claude <noreply@…>

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/routes/posts.js

    rf70e010 r6053c6c  
    713713    // top-level "open the player" link that works even when a browser shield/CSP blocks
    714714    // the cross-site iframe (a full-page navigation is not a cross-site frame).
    715     return { ...p, content, embedHtml, embedUrl };
     715    let poll = null;
     716    if (p.poll_json) { try { poll = JSON.parse(p.poll_json); } catch { /* ignore */ } }
     717    return { ...p, content, embedHtml, embedUrl, poll };
    716718  });
    717719  // Option A: allow the followed Klonkt sites' player iframes (you follow them) by
     
    799801  }
    800802  if (req.get('X-Requested-With') === 'fetch') return res.json({ ok: true, on });
     803  res.redirect('/news');
     804});
     805
     806// Vote on a fediverse poll (a Question in the feed). Owner-only, like the other interactions.
     807router.post('/news/vote', requireSiteManager, async (req, res) => {
     808  const site = res.locals.site;
     809  const note = (req.body.note || '').toString();
     810  let choice = req.body.choice;
     811  if (choice == null) choice = [];
     812  if (!Array.isArray(choice)) choice = [choice];
     813  if (site && note && choice.length) { try { await ActivityPubService.voteOnPoll(site, note, choice.map(String)); } catch (e) { /* ignore */ } }
    801814  res.redirect('/news');
    802815});
Note: See TracChangeset for help on using the changeset viewer.