Changeset b7d4458 in Klonkt for src/services


Ignore:
Timestamp:
06/27/2026 08:10:43 AM (3 months ago)
Author:
Robin Genis <roboburr@…>
Branches:
main
Children:
c21197a
Parents:
92f6976
Message:

feat(nsfw): custom content-warning text + blur in the Cirkel

  • Per-post content-warning text (like Mastodon): editor field; used as the on-site veil/banner label and the fediverse Note summary (falls back to 'Gevoelige inhoud').
  • Cirkel feed now blurs remote sensitive posts: ap_timeline gets nsfw+cw (captured from the incoming Note's sensitive/summary), getCirkelPosts returns them, circle.js maps them so post-card/tile show the veil.
Location:
src/services
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • src/services/ActivityPubService.js

    r92f6976 rb7d4458  
    250250    sensitive: !!post.nsfw,
    251251  };
    252   if (post.nsfw) note.summary = 'Gevoelige inhoud';
     252  if (post.nsfw) note.summary = post.content_warning || 'Gevoelige inhoud';
    253253  if (attachment.length) note.attachment = attachment;
    254254  // Playable-audio posts suppress the cover attachment (player card). Still expose
     
    708708        // the timeline).
    709709        for (const s of subs) {
    710           tlStmts().ins.run(o.id, s.slug, actorUri, ai.name, ai.handle, ai.icon, ai.url, html, o.url || null, o.published || null, media);
     710          tlStmts().ins.run(o.id, s.slug, actorUri, ai.name, ai.handle, ai.icon, ai.url, html, o.url || null, o.published || null, media, o.sensitive ? 1 : 0, o.summary || null);
    711711        }
    712712        console.log('[AP] timeline +', actorUri, 'x' + subs.length);
     
    775775  if (!site) return;
    776776  const recent = db.prepare(
    777     `SELECT id, slug, title, content, cover_image_url, nsfw, published_at, created_at
     777    `SELECT id, slug, title, content, cover_image_url, nsfw, content_warning, published_at, created_at
    778778     FROM posts WHERE site_id = ? AND status = 'published' AND (fan_only IS NULL OR fan_only = 0)
    779779     ORDER BY COALESCE(published_at, created_at) DESC LIMIT 20`
     
    10111011    url: note.url || url,
    10121012    content: HtmlSanitizerService.sanitize(rawHtml),       // full, sanitized
     1013    sensitive: !!note.sensitive,                            // remote CW → blur in the Cirkel
     1014    cw: note.summary || '',
    10131015    images,
    10141016    threadInboxes,                                          // every ancestor author's inbox
     
    10831085function tlStmts() {
    10841086  if (!_insTl) {
    1085     _insTl = db.prepare('INSERT OR IGNORE INTO ap_timeline (id, slug, author_uri, author_name, author_handle, author_icon, author_url, content, url, published, media_json, created_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,CURRENT_TIMESTAMP)');
     1087    _insTl = db.prepare('INSERT OR IGNORE INTO ap_timeline (id, slug, author_uri, author_name, author_handle, author_icon, author_url, content, url, published, media_json, nsfw, cw, created_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,CURRENT_TIMESTAMP)');
    10861088    _listTl = db.prepare('SELECT * FROM ap_timeline WHERE slug = ? ORDER BY COALESCE(published, created_at) DESC LIMIT ?');
    10871089    _delTl = db.prepare('DELETE FROM ap_timeline WHERE id = ?');
     
    11021104    if (!_cirkelPosts) _cirkelPosts = db.prepare(`
    11031105      SELECT t.id, t.author_uri, t.author_name, t.author_handle, t.author_icon, t.author_url,
    1104              t.content, t.url, t.published, t.media_json, t.boosted
     1106             t.content, t.url, t.published, t.media_json, t.boosted, t.nsfw, t.cw
    11051107      FROM ap_timeline t
    11061108      LEFT JOIN ap_following f ON f.slug = t.slug AND f.actor_uri = t.author_uri
     
    11421144    tlStmts().ins.run(id, slug, note.actor_uri || '', note.actor_name || '', note.actor_handle || '',
    11431145      note.actor_icon || '', note.actor_url || '', note.content || '', note.url || null,
    1144       new Date().toISOString(), media);
     1146      new Date().toISOString(), media, note.sensitive ? 1 : 0, note.cw || null);
    11451147  } catch { /* ignore */ }
    11461148  markBoosted(slug, id);
  • src/services/Scheduler.js

    r92f6976 rb7d4458  
    1515  try {
    1616    const due = db.prepare(`
    17       SELECT p.id, p.site_id, p.slug, p.title, p.content, p.cover_image_url, p.fan_only, p.nsfw,
     17      SELECT p.id, p.site_id, p.slug, p.title, p.content, p.cover_image_url, p.fan_only, p.nsfw, p.content_warning,
    1818             p.published_at, p.publish_at, p.created_at, u.username
    1919      FROM posts p JOIN users u ON u.id = p.author_id
     
    3939            id: p.id, slug: p.slug, title: p.title || p.slug,
    4040            content: p.content, cover_image_url: p.cover_image_url || null,
    41             published_at: p.published_at || p.publish_at, created_at: p.created_at, fan_only: p.fan_only, nsfw: p.nsfw,
     41            published_at: p.published_at || p.publish_at, created_at: p.created_at, fan_only: p.fan_only, nsfw: p.nsfw, content_warning: p.content_warning,
    4242          }).catch(() => { /* best-effort */ });
    4343        }
  • src/services/i18n.js

    r92f6976 rb7d4458  
    771771    'pedit.pin_top': 'bovenaan',
    772772    'pedit.pin_nth_suffix': 'e van boven',
    773     'pedit.noindex_label': 'noindex (verberg voor zoekmachines)', 'pedit.nsfw_label': 'NSFW / gevoelige inhoud', 'post.nsfw_warning': 'Gevoelige inhoud', 'post.nsfw_show': 'Tonen',
     773    'pedit.noindex_label': 'noindex (verberg voor zoekmachines)', 'pedit.nsfw_label': 'NSFW / gevoelige inhoud', 'pedit.nsfw_cw_ph': 'Waarschuwingstekst (optioneel, standaard: Gevoelige inhoud)', 'post.nsfw_warning': 'Gevoelige inhoud', 'post.nsfw_show': 'Tonen',
    774774    'pedit.fan_only_label': 'Alleen voor ingelogde fans',
    775775    'pedit.schedule_label': 'Publicatie inplannen',
     
    17821782    'pedit.pin_top': 'at the top',
    17831783    'pedit.pin_nth_suffix': 'th from top',
    1784     'pedit.noindex_label': 'noindex (hide from search engines)', 'pedit.nsfw_label': 'NSFW / sensitive content', 'post.nsfw_warning': 'Sensitive content', 'post.nsfw_show': 'Show',
     1784    'pedit.noindex_label': 'noindex (hide from search engines)', 'pedit.nsfw_label': 'NSFW / sensitive content', 'pedit.nsfw_cw_ph': 'Warning text (optional, default: Sensitive content)', 'post.nsfw_warning': 'Sensitive content', 'post.nsfw_show': 'Show',
    17851785    'pedit.fan_only_label': 'Logged-in fans only',
    17861786    'pedit.schedule_label': 'Schedule publication',
     
    27932793    'pedit.pin_top': 'ganz oben',
    27942794    'pedit.pin_nth_suffix': '. von oben',
    2795     'pedit.noindex_label': 'noindex (vor Suchmaschinen verbergen)', 'pedit.nsfw_label': 'NSFW / sensibler Inhalt', 'post.nsfw_warning': 'Sensibler Inhalt', 'post.nsfw_show': 'Anzeigen',
     2795    'pedit.noindex_label': 'noindex (vor Suchmaschinen verbergen)', 'pedit.nsfw_label': 'NSFW / sensibler Inhalt', 'pedit.nsfw_cw_ph': 'Warntext (optional, Standard: Sensibler Inhalt)', 'post.nsfw_warning': 'Sensibler Inhalt', 'post.nsfw_show': 'Anzeigen',
    27962796    'pedit.fan_only_label': 'Nur für angemeldete Fans',
    27972797    'pedit.schedule_label': 'Veröffentlichung planen',
Note: See TracChangeset for help on using the changeset viewer.