diff --git a/.changeset/smooth-chairs-bow.md b/.changeset/smooth-chairs-bow.md new file mode 100644 index 000000000..1ef49363c --- /dev/null +++ b/.changeset/smooth-chairs-bow.md @@ -0,0 +1,5 @@ +--- +"nostrudel": patch +--- + +Fix link cards breaking lines diff --git a/src/components/embed-types/common.tsx b/src/components/embed-types/common.tsx index bb55a436e..9c7f5581a 100644 --- a/src/components/embed-types/common.tsx +++ b/src/components/embed-types/common.tsx @@ -2,6 +2,7 @@ import { Link } from "@chakra-ui/react"; import OpenGraphCard from "../open-graph-card"; import { isVideoURL } from "../../helpers/url"; +import OpenGraphLink from "../open-graph-link"; export function renderVideoUrl(match: URL) { if (!isVideoURL(match)) return null; @@ -23,6 +24,6 @@ export function renderGenericUrl(match: URL) { ); } -export function renderOpenGraphUrl(match: URL) { - return ; +export function renderOpenGraphUrl(match: URL, isEndOfLine: boolean) { + return isEndOfLine ? : ; } diff --git a/src/components/open-graph-link.tsx b/src/components/open-graph-link.tsx new file mode 100644 index 000000000..7ce7868b1 --- /dev/null +++ b/src/components/open-graph-link.tsx @@ -0,0 +1,12 @@ +import { Link, LinkProps } from "@chakra-ui/react"; +import useOpenGraphData from "../hooks/use-open-graph-data"; + +export default function OpenGraphLink({ url, ...props }: { url: URL } & Omit) { + const { value: data } = useOpenGraphData(url); + + return ( + + {data?.ogTitle?.trim() ?? data?.dcTitle?.trim() ?? decodeURI(url.toString())} + + ); +} diff --git a/src/helpers/embeds.ts b/src/helpers/embeds.ts index 1e7b1474a..7add63f79 100644 --- a/src/helpers/embeds.ts +++ b/src/helpers/embeds.ts @@ -4,7 +4,7 @@ import { getMatchLink } from "./regexp"; export type EmbedableContent = (string | JSX.Element)[]; export type EmbedType = { regexp: RegExp; - render: (match: RegExpMatchArray) => JSX.Element | string | null; + render: (match: RegExpMatchArray, isEndOfLine: boolean) => JSX.Element | string | null; name: string; getLocation?: (match: RegExpMatchArray) => { start: number; end: number }; }; @@ -35,7 +35,8 @@ export function embedJSX(content: EmbedableContent, embed: EmbedType): Embedable const before = str.slice(0, start - cursor); const after = str.slice(end - cursor, str.length); - let render = embed.render(match); + const isEndOfLine = /^\p{Z}*(\n|$)/iu.test(after); + let render = embed.render(match, isEndOfLine); if (render === null) continue; if (typeof render !== "string" && !render.props.key) { @@ -68,18 +69,18 @@ export function embedJSX(content: EmbedableContent, embed: EmbedType): Embedable .flat(); } -export type LinkEmbedHandler = (link: URL) => JSX.Element | string | null; +export type LinkEmbedHandler = (link: URL, isEndOfLine: boolean) => JSX.Element | string | null; export function embedUrls(content: EmbedableContent, handlers: LinkEmbedHandler[]) { return embedJSX(content, { name: "embedUrls", regexp: getMatchLink(), - render: (match) => { + render: (match, isEndOfLine) => { try { const url = new URL(match[0]); for (const handler of handlers) { try { - const content = handler(url); + const content = handler(url, isEndOfLine); if (content) return content; } catch (e) {} }