Changeset e91849c in Klonkt


Ignore:
Timestamp:
06/25/2026 10:21:29 PM (3 months ago)
Author:
Robin Genis <roboburr@…>
Branches:
main
Children:
67fe576
Parents:
2902320
Message:

ux(fedi): owner replies to a fediverse comment directly (no 'your server' detour)

On your own site, the reply button under an inbound fediverse comment showed
the visitor flow (asks for 'your server' and bounces to authorize_interaction).
For the site owner that is pointless — you ARE the server. Owners now get an
inline reply box that posts straight to /posts/:slug/fedi-reply (deliverReply as
the site); visitors keep the remote-interaction popover. Exposes the interaction
id on inbound nodes + threads postSlug through fedi-node.

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

Location:
src
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • src/services/ActivityPubService.js

    r2902320 re91849c  
    410410    if (r.kind !== 'reply') continue;
    411411    nodes.push({
    412       noteId: r.object_uri, parent: r.parent_uri || null, mine: false,
     412      noteId: r.object_uri, parent: r.parent_uri || null, mine: false, id: r.id,
    413413      actor_name: r.actor_name, actor_handle: r.actor_handle, actor_url: r.actor_url,
    414414      actor_icon: r.actor_icon, content: r.content, created_at: r.published || r.created_at,
  • src/views/pages/post.ejs

    r2902320 re91849c  
    9090        <ol class="comments-list">
    9191          <% _fedi.thread.forEach(function(n){ %>
    92             <li class="comment"><%- include('../partials/fedi-node', { n: n, t: t, canManageSite: (typeof canManageSite !== 'undefined' ? canManageSite : false), _base: _base, siteAvatar: (typeof siteAvatar !== 'undefined' ? siteAvatar : null), formatDateTime: formatDateTime }) %></li>
     92            <li class="comment"><%- include('../partials/fedi-node', { n: n, t: t, canManageSite: (typeof canManageSite !== 'undefined' ? canManageSite : false), _base: _base, siteAvatar: (typeof siteAvatar !== 'undefined' ? siteAvatar : null), formatDateTime: formatDateTime, postSlug: post.slug }) %></li>
    9393          <% }); %>
    9494        </ol>
     
    360360   otherwise override the [hidden] attribute → form was always open. */
    361361.comment-reply-form[hidden] { display: none; }
     362.fedi-owner-reply summary { list-style: none; cursor: pointer; }
     363.fedi-owner-reply summary::-webkit-details-marker { display: none; }
     364.fedi-owner-reply[open] summary { margin-bottom: .45rem; }
    362365/* DELETE button is inside a <form>; display:contents makes it a direct flex-sibling
    363366   of REPLY → pixel-perfect alignment + equal gap. */
  • src/views/partials/fedi-node.ejs

    r2902320 re91849c  
    1 <%# Renders one fediverse thread node (n). Expects: n, t, canManageSite, _base, siteAvatar, formatDateTime %>
     1<%# Renders one fediverse thread node (n). Expects: n, t, canManageSite, _base, siteAvatar, formatDateTime, postSlug %>
    22<div class="comment-avatar">
    33  <% if (n.actor_icon) { %><img src="<%= n.actor_icon %>" alt="" loading="lazy">
     
    1717        <button type="submit" class="comment-delete-btn"><%= t('comments.delete') %></button>
    1818      </form>
     19    <% } else if (!n.mine && typeof canManageSite !== 'undefined' && canManageSite && n.id && typeof postSlug !== 'undefined' && postSlug) { %>
     20      <%# Owner: reply directly AS the site — no "your server" detour (it's your own site). %>
     21      <details class="fedi-owner-reply">
     22        <summary class="comment-reply-btn">
     23          <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>
     24          <%= t('fedi.remote_reply_short') %>
     25        </summary>
     26        <form method="post" action="<%= _base %>/posts/<%= postSlug %>/fedi-reply" class="comment-reply-form">
     27          <input type="hidden" name="interaction_id" value="<%= n.id %>">
     28          <textarea name="text" rows="2" required placeholder="<%= t('fedi.reply_ph') %>"></textarea>
     29          <div class="comment-reply-form-actions">
     30            <button type="submit" class="btn"><%= t('fedi.send') %></button>
     31          </div>
     32        </form>
     33      </details>
    1934    <% } else if (!n.mine && n.noteId) { %>
    2035      <button type="button" class="comment-reply-btn fedi-remote-reply-btn" data-fedi-uri="<%= n.noteId %>" data-fedi-ph="<%= t('fedi.remote_ph') %>">
     
    2742    <ol class="comment-replies">
    2843      <% n.children.forEach(function(c){ %>
    29         <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 }) %></li>
     44        <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>
    3045      <% }); %>
    3146    </ol>
Note: See TracChangeset for help on using the changeset viewer.