Changeset 97bacf2 in Klonkt


Ignore:
Timestamp:
07/25/2026 11:32:44 PM (6 weeks ago)
Author:
Robin <roboburr@…>
Branches:
main
Children:
8f8b40f
Parents:
f1c50f9
Message:

FEP-9098 emit: Klonkt bewaart + serveert custom-emoji tags (C2S inbox)

De Shaer-client toonde nog :shortcode: omdat de data ontbrak: Klonkt bewaarde de
Emoji-tags van inkomende federatie-posts niet en serveerde geen tag-array op de
C2S inbox-read. Nu: op inbound Create (en backfill) worden de Emoji-tags bewaard
in ap_timeline.emoji_json, en de inbox-read serveert ze als note.tag zodat de
client de shortcode->url map kan bouwen en de emoji inline rendert.

Changed files:
src/config/database.js

  • ap_timeline.emoji_json

src/services/ActivityPubService.js

  • extractEmojiTags(tag) + timelineEmojis(json); UPDATE emoji_json bij inbound Create + backfill

src/routes/activitypub.js

  • inbox-read serveert nu tag: timelineEmojis(t.emoji_json)

New file:
test/custom-emojis.test.js

  • extract/round-trip + geen-emoji-geval

remarks: npm test 173/173. Werkt voor NIEUWE inbound posts; bestaande timeline-rijen
hebben nog geen emoji_json (geen backfill). Eigen Klonkt-posts hebben geen custom
emojis (geen picker); losse :tekst: zonder gedefinieerde emoji blijft terecht letterlijk.

-robo
Co-Authored-By: Claude Opus 4.8 <noreply@…>

Files:
1 added
3 edited

Legend:

Unmodified
Added
Removed
  • src/config/database.js

    rf1c50f9 r97bacf2  
    547547  ensureColumn('ap_timeline', 'nsfw', 'INTEGER DEFAULT 0');  // remote sensitive post → blur in the Cirkel
    548548  ensureColumn('ap_timeline', 'cw', 'TEXT');                 // remote content-warning text
     549  ensureColumn('ap_timeline', 'emoji_json', 'TEXT');         // FEP-9098 custom emoji Emoji tags from the inbound note, served back as `tag`
    549550  ensureColumn('ap_timeline', 'reblog_name', 'TEXT');        // a followed account boosted this → "X boosted"
    550551  ensureColumn('ap_timeline', 'reblog_handle', 'TEXT');      //   the booster's @handle
  • src/routes/activitypub.js

    rf1c50f9 r97bacf2  
    162162      // client renders their images/audio like own outbox posts.
    163163      attachment: AP.timelineAttachments(t.media_json),
     164      // FEP-9098 custom emojis: the note's Emoji tags, so the client can
     165      // render :shortcode: as an image instead of literal text.
     166      tag: AP.timelineEmojis(t.emoji_json),
    164167    },
    165168  }));
  • src/services/ActivityPubService.js

    rf1c50f9 r97bacf2  
    15371537          // FEP-633c §2.2: register the ward hint on the stored object (no action yet).
    15381538          if (Guardianship.objectHasGuardians(o)) { try { db.prepare('UPDATE ap_timeline SET has_guardians = 1 WHERE id = ? AND slug = ?').run(o.id, s.slug); } catch { /* ignore */ } }
     1539          // FEP-9098: keep the note's custom-emoji tags so the C2S inbox read can serve them.
     1540          { const ej = extractEmojiTags(o.tag); if (ej) { try { db.prepare('UPDATE ap_timeline SET emoji_json = ? WHERE id = ? AND slug = ?').run(ej, o.id, s.slug); } catch { /* ignore */ } } }
    15391541          if (poll) { try { db.prepare('UPDATE ap_timeline SET poll_json = ? WHERE id = ? AND slug = ?').run(JSON.stringify(poll), o.id, s.slug); } catch { /* ignore */ } }
    15401542        }
     
    25652567}
    25662568
     2569// FEP-9098 custom emojis. Inbound: keep the note's Emoji tags (as JSON) so we
     2570// can serve them back. `extractEmojiTags` returns the JSON to store (or null);
     2571// `timelineEmojis` turns the stored JSON back into an AS2 `tag` array for the
     2572// C2S inbox read, so a client (Shaer) can render :shortcode: as an image.
     2573export function extractEmojiTags(tag) {
     2574  const arr = Array.isArray(tag) ? tag : (tag ? [tag] : []);
     2575  const emojis = arr.filter((t) => t && (Array.isArray(t.type) ? t.type[0] : t.type) === 'Emoji'
     2576    && typeof t.name === 'string' && t.icon);
     2577  return emojis.length ? JSON.stringify(emojis) : null;
     2578}
     2579export function timelineEmojis(emojiJson) {
     2580  try { const arr = emojiJson ? JSON.parse(emojiJson) : null; return (Array.isArray(arr) && arr.length) ? arr : undefined; }
     2581  catch { return undefined; }
     2582}
     2583
    25672584// ── Cirkel = posts from the accounts you auto-boost ("feature an artist") ──
    25682585let _abCount, _cirkelPosts, _cirkelMembers;
     
    27202737        const r = tlStmts().ins.run(o.id, slug, actorUri, ai.name, ai.handle, ai.icon, ai.url, html, o.url || null, o.published || null, mediaFromNote(o), o.sensitive ? 1 : 0, o.summary || null);
    27212738        if (r && r.changes > 0) added++;
     2739        // FEP-9098: keep custom-emoji tags from backfilled posts too.
     2740        { const ej = extractEmojiTags(o.tag); if (ej) { try { db.prepare('UPDATE ap_timeline SET emoji_json = ? WHERE id = ? AND slug = ?').run(ej, o.id, slug); } catch { /* ignore */ } } }
    27222741        // Set poll_json if this is a poll and we don't already have it (COALESCE preserves a vote).
    27232742        if (poll) { try { db.prepare('UPDATE ap_timeline SET poll_json = COALESCE(poll_json, ?) WHERE id = ? AND slug = ?').run(JSON.stringify(poll), o.id, slug); } catch { /* ignore */ } }
     
    33573376  getInteractions, getInteractionById, setInteractionBoosted, setInteractionLiked, setMyReaction, getMyReactions, buildReplyNote, getOutboxNote, deliverReply, resolveRemoteNote,
    33583377  listOutbox, deliverOutboxDelete, deliverOutboxUpdate, deliverDirectNote,
    3359   webfingerResolve, followActor, resolveRemoteActor, unfollowActor, listFollowing, setAutoBoost, backfillFromOutbox, getTimeline, timelineAttachments, sendInteraction, voteOnPoll, voteOnRemotePoll,
     3378  webfingerResolve, followActor, resolveRemoteActor, unfollowActor, listFollowing, setAutoBoost, backfillFromOutbox, getTimeline, timelineAttachments, timelineEmojis, sendInteraction, voteOnPoll, voteOnRemotePoll,
    33603379  acceptGatedFollow, rejectGatedFollow, isWardGuardian, sendFollowDecision,
    33613380  parseOwnPoll, pollTally, ownPollView, deliverPollUpdate, maybeCrawlThread, sendReport, localMentionSlugs,
Note: See TracChangeset for help on using the changeset viewer.