source: Klonkt/src/views/partials/fedi-node.ejs@ ef1853c

main
Last change on this file since ef1853c was ef1853c, checked in by Robin Genis <roboburr@…>, 6 weeks ago

Web: custom-emoji ook in messages + threads (Shaer-pariteit)

Na de nieuwsfeed nu ook de andere note-surfaces: messages (msg-item) en
thread-replies (fedi-node) renderen custom-emoji's in de content en in de
auteur-naam. De emoji-tags werden voor replies nog niet bewaard; nu vangt
ap_interactions ze (emoji_json = content-emoji uit o.tag/child.tag,
actor_emoji_json = naam-emoji uit actorInfo) op inbound reply, like/boost en de
thread-crawl, en getNotifications/getInteractions serveren ze mee.

De .emoji-CSS staat nu globaal in shared-styles (was news-only), zodat alle
surfaces 'm delen.

Changed files:
src/config/database.js

  • ap_interactions.emoji_json + actor_emoji_json kolommen

src/services/ActivityPubService.js

  • INSERT ap_interactions + 3 callers (reply/like-boost/thread) vullen de emoji-kolommen
  • emojiJsonOf() helper
  • _listI + getNotifications SELECT en getInteractions-node geven emoji mee

src/views/partials/msg-item.ejs

  • naam + content via emojiName/emojiHtml

src/views/partials/fedi-node.ejs

  • auteur-naam + content via emojiName/emojiHtml

src/views/partials/shared-styles.ejs

  • globale img.emoji-regels (inline, geen media-styling)

src/views/pages/news.ejs

  • dubbele img.emoji-regels weg (nu globaal)

remarks: 187 tests groen. Visueel geverifieerd via standalone render van msg-item
+ fedi-node. Nieuwe replies vangen emoji direct; oude rijen niet (geen self-heal).
Quote-in-replies en de Cirkel-tegel-titels bewust nog niet meegenomen.

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

  • Property mode set to 100644
File size: 7.3 KB
Line 
1<%# Renders one fediverse thread node (n). Expects: n, t, canManageSite, _base, siteAvatar, formatDateTime, postSlug %>
2<div class="comment-avatar">
3 <% if (n.actor_icon) { %><img src="<%= avatar(n.actor_icon, 96) %>" alt="" loading="lazy">
4 <% } else { %><span><%= (n.actor_name || '?').charAt(0).toUpperCase() %></span><% } %>
5</div>
6<div class="comment-body">
7 <div class="comment-meta">
8 <% if (n.actor_url) { %><a class="comment-author" href="<%= n.actor_url %>" rel="nofollow noopener" target="_blank"><%- emojiName(n.actor_name, n.actor_emoji_json) %></a>
9 <% } else { %><span class="comment-author"><%- emojiName(n.actor_name, n.actor_emoji_json) %></span><% } %>
10 <% if (n.actor_handle) { %><span class="fedi-handle"><%= n.actor_handle %></span><% } %>
11 <% if (n.created_at) { %><span class="comment-time"><%= formatDateTime(n.created_at) %></span><% } %>
12 </div>
13 <div class="comment-content"><%- emojiHtml(n.content, n.emoji_json) %></div>
14 <%# Rich replies: media on our own sent replies (attachments on the Note).
15 Visitors see these too, and the reply editor (owner-only) may not render,
16 so make sure the stylesheet is on the page either way. %>
17 <% if (n.media && n.media.length) { %>
18 <% if (!locals.__reAssets) { locals.__reAssets = 1; %><link rel="stylesheet" href="/assets/css/reply-editor.css"><% } %>
19 <div class="re-reply-media">
20 <% n.media.forEach(function (m) { %>
21 <% if ((m.mediaType || '').indexOf('image/') === 0) { %>
22 <a href="<%= m.url %>" target="_blank" rel="noopener"><img src="<%= m.url %>" alt="<%= m.name || '' %>" loading="lazy"></a>
23 <% } else if ((m.mediaType || '').indexOf('audio/') === 0) { %>
24 <audio controls preload="none" src="<%= m.url %>"></audio>
25 <% } else { %>
26 <video controls preload="metadata" src="<%= m.url %>"></video>
27 <% } %>
28 <% }); %>
29 </div>
30 <% } %>
31 <div class="comment-actions">
32 <% if (n.mine && typeof canManageSite !== 'undefined' && canManageSite && n.outboxId) { %>
33 <form method="post" action="<%= _base %>/fediverse/<%= n.outboxId %>/delete" data-confirm="<%= t('fedi.delete_confirm') %>">
34 <button type="submit" class="comment-delete-btn"><%= t('comments.delete') %></button>
35 </form>
36 <% } else if (!n.mine && typeof canManageSite !== 'undefined' && canManageSite && n.id && typeof postSlug !== 'undefined' && postSlug) { %>
37 <%# Owner: like / boost / reply directly AS the site — no "your server" detour (it's your own site). %>
38 <form method="post" action="<%= _base %>/posts/<%= postSlug %>/fedi-react" class="fedi-owner-react">
39 <input type="hidden" name="interaction_id" value="<%= n.id %>">
40 <input type="hidden" name="kind" value="like">
41 <button type="submit" class="fedi-cact fedi-cact-like<%= n.acted_like ? ' is-on' : '' %>" title="<%= n.acted_like ? t('fedi.unlike_short') : t('fedi.like_short') %>" aria-label="<%= n.acted_like ? t('fedi.unlike_short') : t('fedi.like_short') %>"><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></button>
42 </form>
43 <form method="post" action="<%= _base %>/posts/<%= postSlug %>/fedi-react" class="fedi-owner-react">
44 <input type="hidden" name="interaction_id" value="<%= n.id %>">
45 <input type="hidden" name="kind" value="boost">
46 <button type="submit" class="fedi-cact fedi-cact-boost<%= n.acted_boost ? ' is-on' : '' %>" title="<%= n.acted_boost ? t('tl.unboost') : t('fedi.boost_short') %>" aria-label="<%= n.acted_boost ? t('tl.unboost') : t('fedi.boost_short') %>"><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></button>
47 </form>
48 <details class="fedi-owner-reply">
49 <summary class="fedi-cact fedi-cact-reply" title="<%= t('fedi.remote_reply_short') %>" aria-label="<%= t('fedi.remote_reply_short') %>"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="9 17 4 12 9 7"/><path d="M20 18v-2a4 4 0 0 0-4-4H4"/></svg></summary>
50 <%- include('reply-editor', {
51 action: _base + '/posts/' + postSlug + '/fedi-reply',
52 hiddenFields: [{ name: 'interaction_id', value: n.id }],
53 placeholder: t('fedi.reply_ph'),
54 submitLabel: t('fedi.send'),
55 rows: 2,
56 participants: n.participants || [],
57 }) %>
58 </details>
59 <%# Owner moderation: report to their instance + remove (tombstoned: never comes back via re-delivery or thread-crawl). %>
60 <form method="post" action="<%= _base %>/interactions/<%= n.id %>/report" class="fedi-owner-react" data-confirm="<%= t('fedi.mod_report_confirm') %>">
61 <button type="submit" class="fedi-cact fedi-cact-report" title="<%= t('fedi.report_send') %>" aria-label="<%= t('fedi.report_send') %>"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z"/><line x1="4" y1="22" x2="4" y2="15"/></svg></button>
62 </form>
63 <form method="post" action="<%= _base %>/interactions/<%= n.id %>/remove" class="fedi-owner-react" data-confirm="<%= t('fedi.mod_remove_confirm') %>">
64 <button type="submit" class="fedi-cact fedi-cact-remove" title="<%= t('comments.delete') %>" aria-label="<%= t('comments.delete') %>"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M9 6V4a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v2"/></svg></button>
65 </form>
66 <% } else if (n.noteId) { %>
67 <%# Visitor: reply via YOUR OWN instance. Also on the site's own (mine) comments —
68 those are dereferenceable Notes (/ap/notes/<id>) too; without this a visitor
69 could not reply to the owner's comments at all (only the owner branches above
70 catch mine-nodes for managers, so this else-if never renders for the owner). %>
71 <button type="button" class="comment-reply-btn fedi-remote-reply-btn" data-fedi-uri="<%= n.noteId %>" data-fedi-ph="<%= t('fedi.remote_ph') %>">
72 <svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="9 17 4 12 9 7"/><path d="M20 18v-2a4 4 0 0 0-4-4H4"/></svg>
73 <%= t('fedi.remote_reply_short') %>
74 </button>
75 <% } %>
76 </div>
77 <% if (n.children && n.children.length) { %>
78 <ol class="comment-replies">
79 <% n.children.forEach(function(c){ %>
80 <li class="comment comment-reply"><%- include('../partials/fedi-node', { n: c, t: t, canManageSite: (typeof canManageSite !== 'undefined' ? canManageSite : false), _base: _base, siteAvatar: (typeof siteAvatar !== 'undefined' ? siteAvatar : null), formatDateTime: formatDateTime, postSlug: (typeof postSlug !== 'undefined' ? postSlug : null) }) %></li>
81 <% }); %>
82 </ol>
83 <% } %>
84</div>
Note: See TracBrowser for help on using the repository browser.