From c5779cd9b1e669fe6b8220e5c2dff0c125ed04e9 Mon Sep 17 00:00:00 2001 From: Zetaphor Date: Thu, 22 Jun 2023 14:28:32 -0300 Subject: [PATCH] Update community link markdown parsing Remove links without remote servers, add kbin support, add user support --- src/shared/markdown.ts | 120 +++++++++++++++++++---------------------- 1 file changed, 56 insertions(+), 64 deletions(-) diff --git a/src/shared/markdown.ts b/src/shared/markdown.ts index d35b469..f56817e 100644 --- a/src/shared/markdown.ts +++ b/src/shared/markdown.ts @@ -8,7 +8,6 @@ import { CustomEmojiView } from "lemmy-js-client"; import { default as MarkdownIt } from "markdown-it"; import markdown_it_container from "markdown-it-container"; // import markdown_it_emoji from "markdown-it-emoji/bare"; -import { getHttpBase } from "@utils/env"; import markdown_it_footnote from "markdown-it-footnote"; import markdown_it_html5_embed from "markdown-it-html5-embed"; import markdown_it_sub from "markdown-it-sub"; @@ -74,73 +73,66 @@ const html5EmbedConfig = { }; function localCommunityLinkParser(md) { - const pattern = - /(!\b[^@\s]+@[^@\s]+\.[^.\s]+\b)|\/c\/([^@\s]+)(@[^@\s]+\.[^.\s]+\b)?/g; - md.core.ruler.push("replace-text", state => { - const tokens = state.tokens; - - for (let i = 0; i < tokens.length; i++) { - if (tokens[i].type === "inline") { - const token = tokens[i]; - - const originalContent = token.content; - - let lastIndex = 0; - originalContent.replace( - pattern, - (match, fullDomainMatch, name, domainTld, index) => { - let url; - // ex: !Testing@example.com - if (fullDomainMatch) { - const [name, domain, tld] = fullDomainMatch - .slice(1) - .split("@") - .join(".") - .split("."); - url = `${getHttpBase()}/c/${name}@${domain}.${tld}`; + /** + * Accepted formats: + * !community@server.com + * /c/community@server.com + * /m/community@server.com + * /u/username@server.com + */ + const pattern = + /(\/[c|m|u]\/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}|![a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/g; + + for (let i = 0; i < state.tokens.length; i++) { + if (state.tokens[i].type !== "inline") { + continue; + } + const inlineTokens = state.tokens[i].children; + for (let j = inlineTokens.length - 1; j >= 0; j--) { + if ( + inlineTokens[j].type === "text" && + pattern.test(inlineTokens[j].content) + ) { + const textParts = inlineTokens[j].content.split(pattern); + const newTokens: Token[] = []; + + for (const part of textParts) { + let linkClass = "community-link"; + if (pattern.test(part)) { + // Rewrite !community@server.com and KBin /m/community@server.com to local urls + let href; + if (part.startsWith("!")) { + href = "/c/" + part.substring(1); + } else if (part.startsWith("/m/")) { + href = "/c/" + part.substring(3); + } else { + href = part; + if (part.startsWith("/u/")) { + linkClass = "user-link"; + } + } + + const linkOpenToken = new state.Token("link_open", "a", 1); + linkOpenToken.attrs = [ + ["href", href], + ["class", linkClass], + ]; + const textToken = new state.Token("text", "", 0); + textToken.content = part; + const linkCloseToken = new state.Token("link_close", "a", -1); + + newTokens.push(linkOpenToken, textToken, linkCloseToken); } else { - // ex: /c/Testing or /c/Testing@example.com - url = `${getHttpBase()}/c/${name}${domainTld || ""}`; + const textToken = new state.Token("text", "", 0); + textToken.content = part; + newTokens.push(textToken); } - - const beforeContent = originalContent.slice(lastIndex, index); - lastIndex = index + match.length; - - const beforeToken = new state.Token("text", "", 0); - beforeToken.content = beforeContent; - - const linkOpenToken = new state.Token("link_open", "a", 1); - linkOpenToken.attrs = [ - ["href", url], - ["class", "community-link"], - ]; - - const textToken = new state.Token("text", "", 0); - textToken.content = match; - - const linkCloseToken = new state.Token("link_close", "a", -1); - - const afterContent = originalContent.slice(lastIndex); - const afterToken = new state.Token("text", "", 0); - afterToken.content = afterContent; - - tokens.splice(i, 1); - - tokens.splice( - i, - 0, - beforeToken, - linkOpenToken, - textToken, - linkCloseToken, - afterToken - ); - - // Update i to skip the newly added tokens - i += 4; } - ); + + // Replace the original token with the new tokens + inlineTokens.splice(j, 1, ...newTokens); + } } } }); -- 2.44.1