use global regexp for embeds

This commit is contained in:
hzrd149 2023-08-14 10:53:02 -05:00
parent ce550f588c
commit 6752b80c0d
8 changed files with 48 additions and 25 deletions

View File

@ -4,7 +4,7 @@ import { DraftNostrEvent, NostrEvent } from "../../types/nostr-event";
export function embedEmoji(content: EmbedableContent, note: NostrEvent | DraftNostrEvent) {
return embedJSX(content, {
regexp: /:([a-zA-Z0-9_]+):/i,
regexp: /:([a-zA-Z0-9_]+):/gi,
render: (match) => {
const emojiTag = note.tags.find(
(tag) => tag[0] === "emoji" && tag[1].toLowerCase() === match[1].toLowerCase() && tag[2]

View File

@ -4,7 +4,7 @@ import { InlineInvoiceCard } from "../inline-invoice-card";
export function embedLightningInvoice(content: EmbedableContent) {
return embedJSX(content, {
name: "Lightning Invoice",
regexp: /(lightning:)?(LNBC[A-Za-z0-9]+)/im,
regexp: /(lightning:)?(LNBC[A-Za-z0-9]+)/gim,
render: (match) => <InlineInvoiceCard paymentRequest={match[2]} />,
});
}

View File

@ -40,7 +40,7 @@ export function embedNostrLinks(content: EmbedableContent) {
export function embedNostrMentions(content: EmbedableContent, event: NostrEvent | DraftNostrEvent) {
return embedJSX(content, {
name: "nostr-mention",
regexp: /#\[(\d+)\]/,
regexp: /#\[(\d+)\]/g,
render: (match) => {
const index = parseInt(match[1]);
const tag = event?.tags[index];

View File

@ -20,24 +20,43 @@ export function embedJSX(content: EmbedableContent, embed: EmbedType): Embedable
return content
.map((subContent, i) => {
if (typeof subContent === "string") {
const match = subContent.match(embed.regexp);
const matches = subContent.matchAll(embed.regexp);
if (match && match.index !== undefined) {
const { start, end } = (embed.getLocation || defaultGetLocation)(match);
const before = subContent.slice(0, start);
const after = subContent.slice(end, subContent.length);
let render = embed.render(match);
if (matches) {
const newContent: EmbedableContent = [];
let cursor = 0;
let str = subContent;
for (const match of matches) {
if (match.index !== undefined) {
const { start, end } = (embed.getLocation || defaultGetLocation)(match);
if (render === null) return subContent;
if (start < cursor) continue;
if (typeof render !== "string" && !render.props.key) {
render = cloneElement(render, { key: match[0] });
const before = str.slice(0, start - cursor);
const after = str.slice(end - cursor, str.length);
let render = embed.render(match);
if (render === null) continue;
if (typeof render !== "string" && !render.props.key) {
render = cloneElement(render, { key: embed.name + match[0] });
}
newContent.push(before, render);
cursor = end;
str = after;
}
}
const newContent: EmbedableContent = [];
if (before.length > 0) newContent.push(...embedJSX([before], embed));
newContent.push(render);
if (after.length > 0) newContent.push(...embedJSX([after], embed));
// if all matches failed just return the existing content
if (newContent.length === 0) {
return subContent;
}
// add the remaining string to the content
if (str.length > 0) {
newContent.push(str);
}
return newContent;
}
@ -53,7 +72,7 @@ export type LinkEmbedHandler = (link: URL) => JSX.Element | string | null;
export function embedUrls(content: EmbedableContent, handlers: LinkEmbedHandler[]) {
return embedJSX(content, {
name: "embedUrls",
regexp: /https?:\/\/([a-zA-Z0-9\.\-]+\.[a-zA-Z]+)([\p{Letter}\p{Number}&\.-\/\?=#\-@%\+_,:]*)/iu,
regexp: /https?:\/\/([a-zA-Z0-9\.\-]+\.[a-zA-Z]+)([\p{Letter}\p{Number}&\.-\/\?=#\-@%\+_,:]*)/giu,
render: (match) => {
try {
const url = new URL(match[0]);

View File

@ -2,5 +2,5 @@ export const mentionNpubOrNote = /(?:\s|^)(@|nostr:)?((npub1|note1)[qpzry9x8gf2t
export const matchImageUrls =
/https?:\/\/([\dA-z\.-]+\.[A-z\.]{2,12})((?:\/[\+~%\/\.\w\-_]*)?\.(?:svg|gif|png|jpg|jpeg|webp|avif))(\??(?:[\?#\-\+=&;%@\.\w_]*)#?(?:[\-\.\!\/\\\w]*))?/i;
export const matchNostrLink = /(nostr:|@)?((npub|note|nprofile|nevent)1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{58,})/i;
export const matchHashtag = /(^|[^\p{L}])#([\p{L}\p{N}]+)/iu;
export const matchNostrLink = /(nostr:|@)?((npub|note|nprofile|nevent)1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{58,})/gi;
export const matchHashtag = /(^|[^\p{L}])#([\p{L}\p{N}]+)/giu;

View File

@ -27,7 +27,7 @@ export const ThreadPost = ({ post, initShowReplies, focusId }: ThreadItemProps)
{showReplyForm.isOpen && (
<ReplyForm item={post} onCancel={showReplyForm.onClose} onSubmitted={showReplyForm.onClose} />
)}
<ButtonGroup variant="ghost" size="sm" alignSelf="flex-start">
<ButtonGroup variant="link" size="sm" alignSelf="flex-start">
{!showReplyForm.isOpen && (
<Button onClick={showReplyForm.onOpen} leftIcon={<ReplyIcon />}>
Write relay

View File

@ -53,7 +53,7 @@ export const Metadata = ({ name, children }: { name: string } & PropsWithChildre
</div>
) : null;
export function RelayMetadata({ url }: { url: string }) {
export function RelayMetadata({ url, extended }: { url: string; extended?: boolean }) {
const { info } = useRelayInfo(url);
return (
@ -62,13 +62,17 @@ export function RelayMetadata({ url }: { url: string }) {
{info?.pubkey && (
<Flex gap="2" alignItems="center">
<B>Owner:</B>
<UserAvatar pubkey={info.pubkey} size="xs" />
<UserAvatar pubkey={info.pubkey} size="xs" noProxy />
<UserLink pubkey={info.pubkey} />
<UserDnsIdentityIcon pubkey={info.pubkey} onlyIcon />
</Flex>
)}
<Metadata name="Software">{info?.software}</Metadata>
<Metadata name="Version">{info?.version}</Metadata>
{extended && (
<>
<Metadata name="Software">{info?.software}</Metadata>
<Metadata name="Version">{info?.version}</Metadata>
</>
)}
</Box>
);
}

View File

@ -58,7 +58,7 @@ function RelayPage({ relay }: { relay: string }) {
<RelayJoinAction url={relay} />
</ButtonGroup>
</Flex>
<RelayMetadata url={relay} />
<RelayMetadata url={relay} extended />
{info?.supported_nips && <SupportedNIPs nips={info?.supported_nips} />}
<Tabs display="flex" flexDirection="column" flexGrow="1" isLazy colorScheme="brand">
<TabList overflowX="auto" overflowY="hidden" flexShrink={0}>