Ignore:
Timestamp:
08/08/2026 05:23:46 PM (4 weeks ago)
Author:
Robin <roboburr@…>
Branches:
main
Children:
d1075a1
Parents:
abcf51a
Message:

De kaart op je eigen post, en de composer-preview (shaer-k3f)

Het bead-uitgangspunt "serverkant is klaar" bleek half waar: AP-quotes
kregen bij publiceren een quote_uri, maar eigen posts hadden geen kolom
voor een kaart-snapshot en de outbox serveerde geen shaer:quote of
shaer:embed -- de app kon een eigen post dus nooit als kaart tonen, ook
na publicatie niet.

Drie stukken, allemaal langs de pijplijn die er al was:

OPSLAAN deliverCreate resolvet de eerste externe link net als de

inbox dat voor binnenkomende posts doet: een fediverse-
object wordt een quote-snapshot (resolveQuoteByUri, de kern
van resolveQuote maar vanaf een kale URI), anders probeert
de link een externe kaart (resolveExternalEmbed). In
posts.quote_json/embed_json, VOOR de vroege return: ook een
post zonder volgers hoort zijn kaart te krijgen, de app
leest hem uit de outbox en niet uit een bezorging.

SERVEREN de friend-poot van de outbox draagt shaer:quote en -- alleen

voor de bearer en langs zijn eigen embeds-poort --
shaer:embed. Een remote vriend krijgt de embed niet: diens
server resolvet en gate zelf bij ontvangst.

PREVIEW GET /ap/users/:slug/card?url= (bearer-only): een URL langs

exact dezelfde pijplijn, zodat de preview in de composer
nooit iets belooft dat de post niet waarmaakt. Zelfde
gate-regels als de tijdlijn.

Drie tests met gestubde fetch: extern -> embed-snapshot, fediverse ->
quote-snapshot (en niet allebei), en de preview die voor dezelfde links
dezelfde kaarten geeft. Alle 723 groen.

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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/services/ActivityPubService.js

    rabcf51a r4838760  
    29552955      post2 = { ...post2, quote_uri: q.uri, quote_actor: q.actor || null };
    29562956    }
     2957  }
     2958  // De kaart op de eigen post (shaer-k3f), langs dezelfde pijplijn als een
     2959  // binnenkomende: een fediverse-quote wordt een quote-snapshot, anders
     2960  // probeert de link een externe kaart. VOOR de vroege return hieronder, want
     2961  // ook een post zonder volgers hoort zijn kaart te krijgen -- de app leest
     2962  // hem uit de outbox, niet uit een bezorging. Best-effort en eenmalig: wat
     2963  // hier niet lukt blijft een kale link, precies wat het was.
     2964  if (!post2.quote_json && !post2.embed_json) {
     2965    try {
     2966      if (post2.quote_uri) {
     2967        const qj = await resolveQuoteByUri(post2.quote_uri);
     2968        if (qj) { db.prepare('UPDATE posts SET quote_json = ? WHERE id = ?').run(qj, post.id); post2 = { ...post2, quote_json: qj }; }
     2969      } else {
     2970        const ej = await resolveExternalEmbed(post2.content || '');
     2971        if (ej) { db.prepare('UPDATE posts SET embed_json = ? WHERE id = ?').run(ej, post.id); post2 = { ...post2, embed_json: ej }; }
     2972      }
     2973    } catch { /* een kaart is nooit een blokkade voor de post zelf */ }
    29572974  }
    29582975  if (post2.quote_actor) {
     
    49965013}
    49975014
     5015/**
     5016 * De composer-preview (shaer-k3f): één URL langs exact dezelfde pijplijn als
     5017 * publiceren, zodat wat de preview toont ook is wat de post krijgt. Twee
     5018 * uitkomsten, hoogstens een gevuld: een AP-object wordt een quote-snapshot,
     5019 * een externe link probeert een kaart. Beide als JSON-string, dezelfde vorm
     5020 * als de kolommen -- de route serveert ze door timelineQuote/timelineEmbed en
     5021 * de gate, net als de tijdlijn.
     5022 */
     5023export async function previewCard(url) {
     5024  if (!/^https?:\/\//i.test(String(url || ''))) return {};
     5025  const html = `<a href="${String(url).replace(/"/g, '&quot;')}">x</a>`;
     5026  const q = await resolveOwnQuote(html);
     5027  if (q && q.uri) {
     5028    const quoteJson = await resolveQuoteByUri(q.uri).catch(() => null);
     5029    if (quoteJson) return { quoteJson };
     5030  } else {
     5031    const embedJson = await resolveExternalEmbed(html).catch(() => null);
     5032    if (embedJson) return { embedJson };
     5033  }
     5034  return {};
     5035}
     5036
    49985037/** The first http(s) link in sanitized note HTML that is not a mention/hashtag. */
    49995038export function firstExternalUrl(html) {
     
    50515090  const url = quoteHrefOf(note);
    50525091  if (!url) return null;
     5092  return resolveQuoteByUri(url);
     5093}
     5094
     5095/** Hetzelfde snapshot, maar vanaf een kale URI: eigen posts en de
     5096 *  composer-preview (shaer-k3f) kennen alleen de link, niet de tag-vorm. */
     5097async function resolveQuoteByUri(url) {
    50535098  const q = await apGetJson(url);
    50545099  if (!q || typeof q !== 'object') return null;
     
    62706315  acceptGatedFollow, rejectGatedFollow, isWardGuardian, outboxAudience, sendFollowDecision,
    62716316  gateOutgoingFollow, performApprovedFollow,
    6272   parseOwnPoll, pollTally, ownPollView, deliverPollUpdate, maybeCrawlThread, sendReport, localMentionSlugs,
     6317  parseOwnPoll, pollTally, ownPollView, deliverPollUpdate, maybeCrawlThread, sendReport, localMentionSlugs, previewCard,
    62736318  autoBoostCount, boostedCount, setReaction, getReaction, getReactionsFor, canonicalReactionUri, migrateReactions, upsertBoostedNote, getCirkelPosts, getCirkelMembers, selfHealTimeline,
    62746319  getNotifications, listBlocks, isBlockedAny, blockTarget, unblock,
Note: See TracChangeset for help on using the changeset viewer.