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

main
Last change on this file since 2d66d66 was c867b7b, checked in by Robin <roboburr@…>, 7 weeks ago

Fix: visitors can reply to the site owner's own comments

The visitor 'reply via the fediverse' button only rendered on !mine thread
nodes, so the site's own comments offered visitors no action at all: you could
not answer the author's comment from your own instance (reported by Robin on
deno.klonkt.com, replying as sound-fabrics.com). The else-if now also renders
for mine-nodes with a noteId; owners never reach it (their mine/!mine branches
match first) and the own-reply note URI (/ap/notes/<id>) is AP-dereferenceable,
verified. Visitor view verified: the owner's comment now carries the button
with its note URI.

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

  • Property mode set to 100644
File size: 6.4 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"><%= n.actor_name %></a>
9 <% } else { %><span class="comment-author"><%= n.actor_name %></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"><%- n.content %></div>
14 <div class="comment-actions">
15 <% if (n.mine && typeof canManageSite !== 'undefined' && canManageSite && n.outboxId) { %>
16 <form method="post" action="<%= _base %>/fediverse/<%= n.outboxId %>/delete" data-confirm="<%= t('fedi.delete_confirm') %>">
17 <button type="submit" class="comment-delete-btn"><%= t('comments.delete') %></button>
18 </form>
19 <% } else if (!n.mine && typeof canManageSite !== 'undefined' && canManageSite && n.id && typeof postSlug !== 'undefined' && postSlug) { %>
20 <%# Owner: like / boost / reply directly AS the site — no "your server" detour (it's your own site). %>
21 <form method="post" action="<%= _base %>/posts/<%= postSlug %>/fedi-react" class="fedi-owner-react">
22 <input type="hidden" name="interaction_id" value="<%= n.id %>">
23 <input type="hidden" name="kind" value="like">
24 <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>
25 </form>
26 <form method="post" action="<%= _base %>/posts/<%= postSlug %>/fedi-react" class="fedi-owner-react">
27 <input type="hidden" name="interaction_id" value="<%= n.id %>">
28 <input type="hidden" name="kind" value="boost">
29 <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>
30 </form>
31 <details class="fedi-owner-reply">
32 <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>
33 <form method="post" action="<%= _base %>/posts/<%= postSlug %>/fedi-reply" class="comment-reply-form">
34 <input type="hidden" name="interaction_id" value="<%= n.id %>">
35 <textarea name="text" rows="2" required placeholder="<%= t('fedi.reply_ph') %>"></textarea>
36 <div class="comment-reply-form-actions">
37 <button type="submit" class="btn"><%= t('fedi.send') %></button>
38 </div>
39 </form>
40 </details>
41 <%# Owner moderation: report to their instance + remove (tombstoned: never comes back via re-delivery or thread-crawl). %>
42 <form method="post" action="<%= _base %>/interactions/<%= n.id %>/report" class="fedi-owner-react" data-confirm="<%= t('fedi.mod_report_confirm') %>">
43 <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>
44 </form>
45 <form method="post" action="<%= _base %>/interactions/<%= n.id %>/remove" class="fedi-owner-react" data-confirm="<%= t('fedi.mod_remove_confirm') %>">
46 <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>
47 </form>
48 <% } else if (n.noteId) { %>
49 <%# Visitor: reply via YOUR OWN instance. Also on the site's own (mine) comments —
50 those are dereferenceable Notes (/ap/notes/<id>) too; without this a visitor
51 could not reply to the owner's comments at all (only the owner branches above
52 catch mine-nodes for managers, so this else-if never renders for the owner). %>
53 <button type="button" class="comment-reply-btn fedi-remote-reply-btn" data-fedi-uri="<%= n.noteId %>" data-fedi-ph="<%= t('fedi.remote_ph') %>">
54 <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>
55 <%= t('fedi.remote_reply_short') %>
56 </button>
57 <% } %>
58 </div>
59 <% if (n.children && n.children.length) { %>
60 <ol class="comment-replies">
61 <% n.children.forEach(function(c){ %>
62 <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>
63 <% }); %>
64 </ol>
65 <% } %>
66</div>
Note: See TracBrowser for help on using the repository browser.