mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 11:29:46 +00:00
Return properly formatted mentions in Status API
This commit is contained in:
parent
a24c119c7b
commit
529e61be6d
2 changed files with 48 additions and 56 deletions
|
|
@ -4,40 +4,12 @@ import linkify from 'linkifyjs';
|
||||||
import { nip19, nip21, nip27 } from 'nostr-tools';
|
import { nip19, nip21, nip27 } from 'nostr-tools';
|
||||||
|
|
||||||
import { Conf } from '@/config.ts';
|
import { Conf } from '@/config.ts';
|
||||||
|
import { MastodonMention } from '@/entities/MastodonMention.ts';
|
||||||
import { getUrlMediaType, isPermittedMediaType } from '@/utils/media.ts';
|
import { getUrlMediaType, isPermittedMediaType } from '@/utils/media.ts';
|
||||||
|
|
||||||
linkify.registerCustomProtocol('nostr', true);
|
linkify.registerCustomProtocol('nostr', true);
|
||||||
linkify.registerCustomProtocol('wss');
|
linkify.registerCustomProtocol('wss');
|
||||||
|
|
||||||
const linkifyOpts: linkify.Opts = {
|
|
||||||
render: {
|
|
||||||
hashtag: ({ content }) => {
|
|
||||||
const tag = content.replace(/^#/, '');
|
|
||||||
const href = Conf.local(`/tags/${tag}`);
|
|
||||||
return `<a class=\"mention hashtag\" href=\"${href}\" rel=\"tag\"><span>#</span>${tag}</a>`;
|
|
||||||
},
|
|
||||||
url: ({ attributes, content }) => {
|
|
||||||
try {
|
|
||||||
const { decoded } = nip21.parse(content);
|
|
||||||
const pubkey = getDecodedPubkey(decoded);
|
|
||||||
if (pubkey) {
|
|
||||||
const name = pubkey.substring(0, 8);
|
|
||||||
const href = Conf.local(`/users/${pubkey}`);
|
|
||||||
return `<span class="h-card"><a class="u-url mention" href="${href}" rel="ugc">@<span>${name}</span></a></span>`;
|
|
||||||
} else {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
const attr = Object.entries(attributes)
|
|
||||||
.map(([name, value]) => `${name}="${value}"`)
|
|
||||||
.join(' ');
|
|
||||||
|
|
||||||
return `<a ${attr}>${content}</a>`;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
type Link = ReturnType<typeof linkify.find>[0];
|
type Link = ReturnType<typeof linkify.find>[0];
|
||||||
|
|
||||||
interface ParsedNoteContent {
|
interface ParsedNoteContent {
|
||||||
|
|
@ -48,12 +20,42 @@ interface ParsedNoteContent {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Convert Nostr content to Mastodon API HTML. Also return parsed data. */
|
/** Convert Nostr content to Mastodon API HTML. Also return parsed data. */
|
||||||
function parseNoteContent(content: string): ParsedNoteContent {
|
function parseNoteContent(content: string, mentions: MastodonMention[]): ParsedNoteContent {
|
||||||
// Parsing twice is ineffecient, but I don't know how to do only once.
|
|
||||||
const html = linkifyStr(content, linkifyOpts).replace(/\n+$/, '');
|
|
||||||
const links = linkify.find(content).filter(isLinkURL);
|
const links = linkify.find(content).filter(isLinkURL);
|
||||||
const firstUrl = links.find(isNonMediaLink)?.href;
|
const firstUrl = links.find(isNonMediaLink)?.href;
|
||||||
|
|
||||||
|
const html = linkifyStr(content, {
|
||||||
|
render: {
|
||||||
|
hashtag: ({ content }) => {
|
||||||
|
const tag = content.replace(/^#/, '');
|
||||||
|
const href = Conf.local(`/tags/${tag}`);
|
||||||
|
return `<a class=\"mention hashtag\" href=\"${href}\" rel=\"tag\"><span>#</span>${tag}</a>`;
|
||||||
|
},
|
||||||
|
url: ({ attributes, content }) => {
|
||||||
|
try {
|
||||||
|
const { decoded } = nip21.parse(content);
|
||||||
|
const pubkey = getDecodedPubkey(decoded);
|
||||||
|
if (pubkey) {
|
||||||
|
const mention = mentions.find((m) => m.id === pubkey);
|
||||||
|
const npub = nip19.npubEncode(pubkey);
|
||||||
|
const acct = mention?.acct ?? npub;
|
||||||
|
const name = mention?.acct ?? npub.substring(0, 8);
|
||||||
|
const href = mention?.url ?? Conf.local(`/@${acct}`);
|
||||||
|
return `<span class="h-card"><a class="u-url mention" href="${href}" rel="ugc">@<span>${name}</span></a></span>`;
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
const attr = Object.entries(attributes)
|
||||||
|
.map(([name, value]) => `${name}="${value}"`)
|
||||||
|
.join(' ');
|
||||||
|
|
||||||
|
return `<a ${attr}>${content}</a>`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}).replace(/\n+$/, '');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
html,
|
html,
|
||||||
links,
|
links,
|
||||||
|
|
|
||||||
|
|
@ -46,13 +46,14 @@ async function renderStatus(event: DittoEvent, opts: RenderStatusOpts): Promise<
|
||||||
[{ kinds: [0], authors: mentionedPubkeys, limit: mentionedPubkeys.length }],
|
[{ kinds: [0], authors: mentionedPubkeys, limit: mentionedPubkeys.length }],
|
||||||
);
|
);
|
||||||
|
|
||||||
const { html, links, firstUrl } = parseNoteContent(stripimeta(event.content, event.tags));
|
const mentions = await Promise.all(
|
||||||
|
mentionedPubkeys.map((pubkey) => renderMention(pubkey, mentionedProfiles.find((event) => event.pubkey === pubkey))),
|
||||||
|
);
|
||||||
|
|
||||||
const [mentions, card, relatedEvents] = await Promise
|
const { html, links, firstUrl } = parseNoteContent(stripimeta(event.content, event.tags), mentions);
|
||||||
|
|
||||||
|
const [card, relatedEvents] = await Promise
|
||||||
.all([
|
.all([
|
||||||
Promise.all(
|
|
||||||
mentionedPubkeys.map((pubkey) => toMention(pubkey, mentionedProfiles.find((event) => event.pubkey === pubkey))),
|
|
||||||
),
|
|
||||||
firstUrl ? unfurlCardCached(firstUrl) : null,
|
firstUrl ? unfurlCardCached(firstUrl) : null,
|
||||||
viewerPubkey
|
viewerPubkey
|
||||||
? await store.query([
|
? await store.query([
|
||||||
|
|
@ -152,25 +153,14 @@ async function renderReblog(event: DittoEvent, opts: RenderStatusOpts): Promise<
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function toMention(pubkey: string, event?: NostrEvent): Promise<MastodonMention> {
|
async function renderMention(pubkey: string, event?: NostrEvent): Promise<MastodonMention> {
|
||||||
const account = event ? await renderAccount(event) : undefined;
|
const account = event ? await renderAccount(event) : await accountFromPubkey(pubkey);
|
||||||
|
return {
|
||||||
if (account) {
|
id: account.id,
|
||||||
return {
|
acct: account.acct,
|
||||||
id: account.id,
|
username: account.username,
|
||||||
acct: account.acct,
|
url: account.url,
|
||||||
username: account.username,
|
};
|
||||||
url: account.url,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
const npub = nip19.npubEncode(pubkey);
|
|
||||||
return {
|
|
||||||
id: pubkey,
|
|
||||||
acct: npub,
|
|
||||||
username: npub.substring(0, 8),
|
|
||||||
url: Conf.local(`/users/${pubkey}`),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildInlineRecipients(mentions: MastodonMention[]): string {
|
function buildInlineRecipients(mentions: MastodonMention[]): string {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue