diff --git a/src/utils/note.ts b/src/utils/note.ts
index 6e0d8d41..00be4b1a 100644
--- a/src/utils/note.ts
+++ b/src/utils/note.ts
@@ -4,40 +4,12 @@ import linkify from 'linkifyjs';
import { nip19, nip21, nip27 } from 'nostr-tools';
import { Conf } from '@/config.ts';
+import { MastodonMention } from '@/entities/MastodonMention.ts';
import { getUrlMediaType, isPermittedMediaType } from '@/utils/media.ts';
linkify.registerCustomProtocol('nostr', true);
linkify.registerCustomProtocol('wss');
-const linkifyOpts: linkify.Opts = {
- render: {
- hashtag: ({ content }) => {
- const tag = content.replace(/^#/, '');
- const href = Conf.local(`/tags/${tag}`);
- return `#${tag}`;
- },
- 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 `@${name}`;
- } else {
- return '';
- }
- } catch {
- const attr = Object.entries(attributes)
- .map(([name, value]) => `${name}="${value}"`)
- .join(' ');
-
- return `${content}`;
- }
- },
- },
-};
-
type Link = ReturnType[0];
interface ParsedNoteContent {
@@ -48,12 +20,42 @@ interface ParsedNoteContent {
}
/** Convert Nostr content to Mastodon API HTML. Also return parsed data. */
-function parseNoteContent(content: string): ParsedNoteContent {
- // Parsing twice is ineffecient, but I don't know how to do only once.
- const html = linkifyStr(content, linkifyOpts).replace(/\n+$/, '');
+function parseNoteContent(content: string, mentions: MastodonMention[]): ParsedNoteContent {
const links = linkify.find(content).filter(isLinkURL);
const firstUrl = links.find(isNonMediaLink)?.href;
+ const html = linkifyStr(content, {
+ render: {
+ hashtag: ({ content }) => {
+ const tag = content.replace(/^#/, '');
+ const href = Conf.local(`/tags/${tag}`);
+ return `#${tag}`;
+ },
+ 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 `@${name}`;
+ } else {
+ return '';
+ }
+ } catch {
+ const attr = Object.entries(attributes)
+ .map(([name, value]) => `${name}="${value}"`)
+ .join(' ');
+
+ return `${content}`;
+ }
+ },
+ },
+ }).replace(/\n+$/, '');
+
return {
html,
links,
diff --git a/src/views/mastodon/statuses.ts b/src/views/mastodon/statuses.ts
index 27bffdfb..7583ea95 100644
--- a/src/views/mastodon/statuses.ts
+++ b/src/views/mastodon/statuses.ts
@@ -46,13 +46,14 @@ async function renderStatus(event: DittoEvent, opts: RenderStatusOpts): Promise<
[{ 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([
- Promise.all(
- mentionedPubkeys.map((pubkey) => toMention(pubkey, mentionedProfiles.find((event) => event.pubkey === pubkey))),
- ),
firstUrl ? unfurlCardCached(firstUrl) : null,
viewerPubkey
? await store.query([
@@ -152,25 +153,14 @@ async function renderReblog(event: DittoEvent, opts: RenderStatusOpts): Promise<
};
}
-async function toMention(pubkey: string, event?: NostrEvent): Promise {
- const account = event ? await renderAccount(event) : undefined;
-
- if (account) {
- return {
- id: account.id,
- acct: account.acct,
- 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}`),
- };
- }
+async function renderMention(pubkey: string, event?: NostrEvent): Promise {
+ const account = event ? await renderAccount(event) : await accountFromPubkey(pubkey);
+ return {
+ id: account.id,
+ acct: account.acct,
+ username: account.username,
+ url: account.url,
+ };
}
function buildInlineRecipients(mentions: MastodonMention[]): string {