diff --git a/lib/shared/markdown.js b/lib/shared/markdown.js --- a/lib/shared/markdown.js +++ b/lib/shared/markdown.js @@ -257,6 +257,41 @@ const stripSpoilersFromNotifications = (text: string): string => text.replace(replaceSpoilerRegex, spoilerReplacement); +function stripSpoilersFromMarkdownAST(ast: SingleASTNode[]): SingleASTNode[] { + // Either takes top-level AST, or array of nodes under an items node (list) + return ast.map(replaceSpoilersFromMarkdownAST); +} + +function replaceSpoilersFromMarkdownAST(node: SingleASTNode): SingleASTNode { + const { content, items, type } = node; + if (content && typeof content === 'string') { + // Base case (leaf node) + return node; + } else if (type === 'spoiler') { + // The actual point of this function: replacing the spoilers + return { + type: 'text', + content: spoilerReplacement, + }; + } else if (content) { + // Common case... most nodes nest children with content + // If content isn't a string, it should be an array + return { + ...node, + content: stripSpoilersFromMarkdownAST(content), + }; + } else if (items) { + // Special case for lists, which has a nested array of arrays within items + return { + ...node, + items: items.map(stripSpoilersFromMarkdownAST), + }; + } + throw new Error( + `unexpected Markdown node of type ${type} with no content or items`, + ); +} + export { paragraphRegex, paragraphStripTrailingNewlineRegex, @@ -278,4 +313,5 @@ parseList, matchMentions, stripSpoilersFromNotifications, + stripSpoilersFromMarkdownAST, }; diff --git a/lib/shared/messages/text-message-spec.js b/lib/shared/messages/text-message-spec.js --- a/lib/shared/messages/text-message-spec.js +++ b/lib/shared/messages/text-message-spec.js @@ -20,6 +20,7 @@ type ASTNode, type SingleASTNode, stripSpoilersFromNotifications, + stripSpoilersFromMarkdownAST, } from '../markdown'; import { threadIsGroupChat } from '../thread-utils'; import { stringForUser } from '../user-utils'; @@ -89,8 +90,9 @@ messageTitle({ messageInfo, markdownRules }) { const { text } = messageInfo; const parser = SimpleMarkdown.parserFor(markdownRules); - const ast = parser(text, { disableAutoBlockNewlines: true }); - + const ast = stripSpoilersFromMarkdownAST( + parser(text, { disableAutoBlockNewlines: true }), + ); return getFirstNonQuotedRawLine(ast).trim(); },