Changeset fe97cc3 in Klonkt for src/views


Ignore:
Timestamp:
07/01/2026 10:13:40 PM (2 months ago)
Author:
roboburr <roboburr@…>
Branches:
main
Children:
2abd3d2
Parents:
254939c
Message:

feat(fediverse): notification when mentioned in a non-reply post

An inbound Create whose Mention tag targets one of our actors — but which isn't a
reply to our content — was silently ignored. It's now stored and shown in the
fediverse notifications (@ icon, snippet, link to the original note). Only hrefs on
our own base count (a /ap/users/<slug> path on a remote host is someone else's actor).

  • src/config/database.js — ap_mentions table (per-site, UNIQUE(slug, object_uri)).
  • src/services/ActivityPubService.js — localMentionSlugs(tags, base) detection helper (own-base guard, slug must exist, deduped); the Create branch stores a mention per targeted site; getNotifications includes mentions.
  • src/views/pages/fedi-notifications.ejs — mention item (@ icon, content, view-original link).
  • src/services/i18n.js — notif.mentioned (nl/en/de).
  • test/inbound-mentions.test.js — detection guards + notification surfacing.
  • CHANGELOG(.nl/.de).md — "A mention is now a notification" under Added.

Closes prutfolio-src-xgg.

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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/views/pages/fedi-notifications.ejs

    r254939c rfe97cc3  
    77    <ul class="nt-list">
    88      <% items.forEach(function(n){
    9            var _ic = n.type === 'follow' ? 'follow' : (n.type === 'like' ? 'like' : (n.type === 'announce' ? 'boost' : (n.type === 'report' ? 'report' : 'reply')));
     9           var _ic = n.type === 'follow' ? 'follow' : (n.type === 'like' ? 'like' : (n.type === 'announce' ? 'boost' : (n.type === 'report' ? 'report' : (n.type === 'mention' ? 'mention' : 'reply'))));
    1010      %>
    1111        <li class="nt-item nt-<%= _ic %>">
     
    1515            <% } else if (_ic === 'follow') { %><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><line x1="19" y1="8" x2="19" y2="14"/><line x1="22" y1="11" x2="16" y2="11"/></svg>
    1616            <% } else if (_ic === 'report') { %><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><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>
     17            <% } else if (_ic === 'mention') { %><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-3.92 7.94"/></svg>
    1718            <% } else { %><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg><% } %>
    1819          </span>
     
    2829              <% } else if (n.type === 'announce') { %><%= t('notif.boosted') %>
    2930              <% } else if (n.type === 'report') { %><%= t('notif.reported') %>
     31              <% } else if (n.type === 'mention') { %><%= t('notif.mentioned') %>
    3032              <% } else { %><%= t('notif.replied') %><% } %>
    3133              <% if (n.post_slug) { %><a class="nt-post" href="/<%= n.post_slug %>"><%= n.post_title || n.post_slug %></a><% } %>
     34              <% if (n.type === 'mention' && n.note_url) { %><a class="nt-post" href="<%= n.note_url %>" target="_blank" rel="nofollow noopener"><%= t('tl.view_original') %></a><% } %>
    3235            </div>
    3336            <% if (n.type === 'report' && n.content) { %><div class="nt-content"><%= n.content %></div>
    34             <% } else if (n.type === 'reply' && n.content) { %><div class="nt-content"><%- n.content %></div><% } %>
     37            <% } else if ((n.type === 'reply' || n.type === 'mention') && n.content) { %><div class="nt-content"><%- n.content %></div><% } %>
    3538          </div>
    3639        </li>
     
    5962  .nt-follow .nt-icon, .nt-reply .nt-icon { background: color-mix(in srgb, var(--accent, #888) 16%, transparent); color: var(--accent, #06c); }
    6063  .nt-report .nt-icon { background: color-mix(in srgb, #c0392b 16%, transparent); color: #c0392b; }
     64  .nt-mention .nt-icon { background: color-mix(in srgb, #8e6cf0 16%, transparent); color: #8e6cf0; }
    6165  .nt-body { flex: 1; min-width: 0; }
    6266  .nt-line { display: flex; align-items: baseline; gap: .4rem; }
Note: See TracChangeset for help on using the changeset viewer.