bug: parse legacy tag refs
This commit is contained in:
@ -1,13 +1,12 @@
|
|||||||
import "./Markdown.css";
|
import "./Markdown.css";
|
||||||
|
|
||||||
import { ReactNode, forwardRef, useMemo } from "react";
|
import { ReactNode, forwardRef, useMemo } from "react";
|
||||||
import { parseNostrLink, transformText } from "@snort/system";
|
import { transformText } from "@snort/system";
|
||||||
import { marked, Token } from "marked";
|
import { marked, Token } from "marked";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import markedFootnote, { Footnotes, Footnote, FootnoteRef } from "marked-footnote";
|
import markedFootnote, { Footnotes, Footnote, FootnoteRef } from "marked-footnote";
|
||||||
|
|
||||||
import { ProxyImg } from "Element/ProxyImg";
|
import { ProxyImg } from "Element/ProxyImg";
|
||||||
import Mention from "Element/Embed/Mention";
|
|
||||||
import NostrLink from "Element/Embed/NostrLink";
|
import NostrLink from "Element/Embed/NostrLink";
|
||||||
|
|
||||||
interface MarkdownProps {
|
interface MarkdownProps {
|
||||||
@ -15,11 +14,11 @@ interface MarkdownProps {
|
|||||||
tags?: Array<Array<string>>;
|
tags?: Array<Array<string>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderToken(t: Token | Footnotes | Footnote | FootnoteRef): ReactNode {
|
function renderToken(t: Token | Footnotes | Footnote | FootnoteRef, tags: Array<Array<string>>): ReactNode {
|
||||||
try {
|
try {
|
||||||
switch (t.type) {
|
switch (t.type) {
|
||||||
case "paragraph": {
|
case "paragraph": {
|
||||||
return <p>{t.tokens ? t.tokens.map(renderToken) : t.raw}</p>;
|
return <p>{t.tokens ? t.tokens.map(a => renderToken(a, tags)) : t.raw}</p>;
|
||||||
}
|
}
|
||||||
case "image": {
|
case "image": {
|
||||||
return <ProxyImg src={t.href} />;
|
return <ProxyImg src={t.href} />;
|
||||||
@ -27,17 +26,17 @@ function renderToken(t: Token | Footnotes | Footnote | FootnoteRef): ReactNode {
|
|||||||
case "heading": {
|
case "heading": {
|
||||||
switch (t.depth) {
|
switch (t.depth) {
|
||||||
case 1:
|
case 1:
|
||||||
return <h1>{t.tokens ? t.tokens.map(renderToken) : t.raw}</h1>;
|
return <h1>{t.tokens ? t.tokens.map(a => renderToken(a, tags)) : t.raw}</h1>;
|
||||||
case 2:
|
case 2:
|
||||||
return <h2>{t.tokens ? t.tokens.map(renderToken) : t.raw}</h2>;
|
return <h2>{t.tokens ? t.tokens.map(a => renderToken(a, tags)) : t.raw}</h2>;
|
||||||
case 3:
|
case 3:
|
||||||
return <h3>{t.tokens ? t.tokens.map(renderToken) : t.raw}</h3>;
|
return <h3>{t.tokens ? t.tokens.map(a => renderToken(a, tags)) : t.raw}</h3>;
|
||||||
case 4:
|
case 4:
|
||||||
return <h4>{t.tokens ? t.tokens.map(renderToken) : t.raw}</h4>;
|
return <h4>{t.tokens ? t.tokens.map(a => renderToken(a, tags)) : t.raw}</h4>;
|
||||||
case 5:
|
case 5:
|
||||||
return <h5>{t.tokens ? t.tokens.map(renderToken) : t.raw}</h5>;
|
return <h5>{t.tokens ? t.tokens.map(a => renderToken(a, tags)) : t.raw}</h5>;
|
||||||
case 6:
|
case 6:
|
||||||
return <h6>{t.tokens ? t.tokens.map(renderToken) : t.raw}</h6>;
|
return <h6>{t.tokens ? t.tokens.map(a => renderToken(a, tags)) : t.raw}</h6>;
|
||||||
}
|
}
|
||||||
throw new Error("Invalid heading");
|
throw new Error("Invalid heading");
|
||||||
}
|
}
|
||||||
@ -54,30 +53,30 @@ function renderToken(t: Token | Footnotes | Footnote | FootnoteRef): ReactNode {
|
|||||||
return <hr />;
|
return <hr />;
|
||||||
}
|
}
|
||||||
case "blockquote": {
|
case "blockquote": {
|
||||||
return <blockquote>{t.tokens ? t.tokens.map(renderToken) : t.raw}</blockquote>;
|
return <blockquote>{t.tokens ? t.tokens.map(a => renderToken(a, tags)) : t.raw}</blockquote>;
|
||||||
}
|
}
|
||||||
case "link": {
|
case "link": {
|
||||||
return (
|
return (
|
||||||
<Link to={t.href as string} className="ext" target="_blank">
|
<Link to={t.href as string} className="ext" target="_blank">
|
||||||
{t.tokens ? t.tokens.map(renderToken) : t.raw}
|
{t.tokens ? t.tokens.map(a => renderToken(a, tags)) : t.raw}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
case "list": {
|
case "list": {
|
||||||
if (t.ordered) {
|
if (t.ordered) {
|
||||||
return <ol>{t.items.map(renderToken)}</ol>;
|
return <ol>{(t.items as Token[]).map(a => renderToken(a, tags))}</ol>;
|
||||||
} else {
|
} else {
|
||||||
return <ul>{t.items.map(renderToken)}</ul>;
|
return <ul>{(t.items as Token[]).map(a => renderToken(a, tags))}</ul>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "list_item": {
|
case "list_item": {
|
||||||
return <li>{t.tokens ? t.tokens.map(renderToken) : t.raw}</li>;
|
return <li>{t.tokens ? t.tokens.map(a => renderToken(a, tags)) : t.raw}</li>;
|
||||||
}
|
}
|
||||||
case "em": {
|
case "em": {
|
||||||
return <em>{t.tokens ? t.tokens.map(renderToken) : t.raw}</em>;
|
return <em>{t.tokens ? t.tokens.map(a => renderToken(a, tags)) : t.raw}</em>;
|
||||||
}
|
}
|
||||||
case "del": {
|
case "del": {
|
||||||
return <s>{t.tokens ? t.tokens.map(renderToken) : t.raw}</s>;
|
return <s>{t.tokens ? t.tokens.map(a => renderToken(a, tags)) : t.raw}</s>;
|
||||||
}
|
}
|
||||||
case "footnoteRef": {
|
case "footnoteRef": {
|
||||||
return (
|
return (
|
||||||
@ -94,9 +93,9 @@ function renderToken(t: Token | Footnotes | Footnote | FootnoteRef): ReactNode {
|
|||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
if ("tokens" in t) {
|
if ("tokens" in t) {
|
||||||
return (t.tokens as Array<Token>).map(renderToken);
|
return (t.tokens as Array<Token>).map(a => renderToken(a, tags));
|
||||||
}
|
}
|
||||||
return transformText(t.raw, []).map(v => {
|
return transformText(t.raw, tags).map(v => {
|
||||||
switch (v.type) {
|
switch (v.type) {
|
||||||
case "link": {
|
case "link": {
|
||||||
if (v.content.startsWith("nostr:")) {
|
if (v.content.startsWith("nostr:")) {
|
||||||
@ -106,7 +105,7 @@ function renderToken(t: Token | Footnotes | Footnote | FootnoteRef): ReactNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "mention": {
|
case "mention": {
|
||||||
return <Mention link={parseNostrLink(v.content)} />;
|
return <NostrLink link={v.content} />;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
return v.content;
|
return v.content;
|
||||||
@ -127,7 +126,7 @@ export const Markdown = forwardRef<HTMLDivElement, MarkdownProps>((props: Markdo
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="markdown" ref={ref}>
|
<div className="markdown" ref={ref}>
|
||||||
{parsed.filter(a => a.type !== "footnote" && a.type !== "footnotes").map(a => renderToken(a))}
|
{parsed.filter(a => a.type !== "footnote" && a.type !== "footnotes").map(a => renderToken(a, props.tags ?? []))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -9,6 +9,11 @@ export const DefaultConnectTimeout = 2000;
|
|||||||
// eslint-disable-next-line no-useless-escape
|
// eslint-disable-next-line no-useless-escape
|
||||||
export const HashtagRegex = /(#[^\s!@#$%^&*()=+.\/,\[{\]};:'"?><]+)/g;
|
export const HashtagRegex = /(#[^\s!@#$%^&*()=+.\/,\[{\]};:'"?><]+)/g;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Legacy tag reference regex
|
||||||
|
*/
|
||||||
|
export const TagRefRegex = /(#\[\d+\])/gm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* How long profile cache should be considered valid for
|
* How long profile cache should be considered valid for
|
||||||
*/
|
*/
|
||||||
|
@ -7,8 +7,9 @@ import {
|
|||||||
InvoiceRegex,
|
InvoiceRegex,
|
||||||
MarkdownCodeRegex,
|
MarkdownCodeRegex,
|
||||||
MentionNostrEntityRegex,
|
MentionNostrEntityRegex,
|
||||||
|
TagRefRegex,
|
||||||
} from "./const";
|
} from "./const";
|
||||||
import { validateNostrLink } from "./nostr-link";
|
import { NostrLink, validateNostrLink } from "./nostr-link";
|
||||||
import { splitByUrl } from "./utils";
|
import { splitByUrl } from "./utils";
|
||||||
|
|
||||||
export interface ParsedFragment {
|
export interface ParsedFragment {
|
||||||
@ -176,6 +177,28 @@ function extractHashtags(fragments: Fragment[]) {
|
|||||||
.flat();
|
.flat();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function extractTagRefs(fragments: Fragment[], tags: Array<Array<string>>) {
|
||||||
|
return fragments
|
||||||
|
.map(f => {
|
||||||
|
if (typeof f === "string") {
|
||||||
|
return f.split(TagRefRegex).map(i => {
|
||||||
|
if (i.startsWith("#")) {
|
||||||
|
const tag = tags[Number(i.slice(2, -1))];
|
||||||
|
if (tag) {
|
||||||
|
return {
|
||||||
|
type: "mention",
|
||||||
|
content: `nostr:${NostrLink.fromTag(tag).encode()}`,
|
||||||
|
} as ParsedFragment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return f;
|
||||||
|
})
|
||||||
|
.flat();
|
||||||
|
}
|
||||||
|
|
||||||
function extractCustomEmoji(fragments: Fragment[], tags: Array<Array<string>>) {
|
function extractCustomEmoji(fragments: Fragment[], tags: Array<Array<string>>) {
|
||||||
return fragments
|
return fragments
|
||||||
.map(f => {
|
.map(f => {
|
||||||
@ -225,6 +248,7 @@ function extractMarkdownCode(fragments: Fragment[]): (string | ParsedFragment)[]
|
|||||||
export function transformText(body: string, tags: Array<Array<string>>) {
|
export function transformText(body: string, tags: Array<Array<string>>) {
|
||||||
let fragments = extractLinks([body]);
|
let fragments = extractLinks([body]);
|
||||||
fragments = extractMentions(fragments);
|
fragments = extractMentions(fragments);
|
||||||
|
fragments = extractTagRefs(fragments, tags);
|
||||||
fragments = extractHashtags(fragments);
|
fragments = extractHashtags(fragments);
|
||||||
fragments = extractInvoices(fragments);
|
fragments = extractInvoices(fragments);
|
||||||
fragments = extractCashuTokens(fragments);
|
fragments = extractCashuTokens(fragments);
|
||||||
|
Reference in New Issue
Block a user