From 4ab7a6baa60f9b995a4db9523f05f31f74e81ddb Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Fri, 27 May 2022 14:30:34 -0400 Subject: [PATCH] [Gekidou] commonmark feature parity (#6303) * Initial attempt at search highlighting for Markdown * Remove fonts from highlighted text so that it can be bolded * Add initial task list support * Render markdown checkbox * Switch to Commonmark fork * Use searchPatterns prop * searchPatterns prop at post level * Update app/components/markdown/transform.ts Co-authored-by: Jason Frerich Co-authored-by: Harrison Healey Co-authored-by: Jason Frerich --- app/components/markdown/markdown.tsx | 34 +++++- app/components/markdown/transform.test.ts | 34 ++++-- app/components/markdown/transform.ts | 111 ++++++++++++++---- app/components/post_list/post/body/index.tsx | 5 +- .../post_list/post/body/message/message.tsx | 5 +- app/components/post_list/post/post.tsx | 5 +- app/utils/markdown/index.ts | 5 +- package-lock.json | 79 +++++++++++-- package.json | 4 +- patches/@types+commonmark+0.27.5.patch | 33 ------ types/global/markdown.ts | 7 +- 11 files changed, 234 insertions(+), 88 deletions(-) delete mode 100644 patches/@types+commonmark+0.27.5.patch diff --git a/app/components/markdown/markdown.tsx b/app/components/markdown/markdown.tsx index 69d93f046..9bcd2292a 100644 --- a/app/components/markdown/markdown.tsx +++ b/app/components/markdown/markdown.tsx @@ -6,10 +6,11 @@ import Renderer from 'commonmark-react-renderer'; import React, {ReactElement, useRef} from 'react'; import {Dimensions, GestureResponderEvent, Platform, StyleProp, Text, TextStyle, View} from 'react-native'; +import CompassIcon from '@components/compass_icon'; import Emoji from '@components/emoji'; import FormattedText from '@components/formatted_text'; import Hashtag from '@components/markdown/hashtag'; -import {blendColors, concatStyles, makeStyleSheetFromTheme} from '@utils/theme'; +import {blendColors, changeOpacity, concatStyles, makeStyleSheetFromTheme} from '@utils/theme'; import {getScheme} from '@utils/url'; import AtMention from './at_mention'; @@ -26,11 +27,11 @@ import MarkdownTable from './markdown_table'; import MarkdownTableCell, {MarkdownTableCellProps} from './markdown_table_cell'; import MarkdownTableImage from './markdown_table_image'; import MarkdownTableRow, {MarkdownTableRowProps} from './markdown_table_row'; -import {addListItemIndices, combineTextNodes, highlightMentions, pullOutImages} from './transform'; +import {addListItemIndices, combineTextNodes, highlightMentions, highlightSearchPatterns, parseTaskLists, pullOutImages} from './transform'; import type { MarkdownAtMentionRenderer, MarkdownBaseRenderer, MarkdownBlockStyles, MarkdownChannelMentionRenderer, - MarkdownEmojiRenderer, MarkdownImageRenderer, MarkdownLatexRenderer, MarkdownTextStyles, UserMentionKey, + MarkdownEmojiRenderer, MarkdownImageRenderer, MarkdownLatexRenderer, MarkdownTextStyles, SearchPattern, UserMentionKey, } from '@typings/global/markdown'; type MarkdownProps = { @@ -55,6 +56,7 @@ type MarkdownProps = { minimumHashtagLength?: number; onPostPress?: (event: GestureResponderEvent) => void; postId?: string; + searchPatterns?: SearchPattern[]; textStyles?: MarkdownTextStyles; theme: Theme; value?: string | number; @@ -100,6 +102,10 @@ const getExtraPropsForNode = (node: any) => { extraProps.size = node.size; } + if (node.type === 'checkbox') { + extraProps.isChecked = node.isChecked; + } + return extraProps; }; @@ -113,7 +119,7 @@ const Markdown = ({ disableAtChannelMentionHighlight = false, disableAtMentions = false, disableChannelLink = false, disableGallery = false, disableHashtags = false, enableInlineLatex, enableLatex, imagesMetadata, isEdited, isReplyPost, isSearchResult, layoutWidth, - location, mentionKeys, minimumHashtagLength = 3, onPostPress, postId, + location, mentionKeys, minimumHashtagLength = 3, onPostPress, postId, searchPatterns, textStyles = {}, theme, value = '', }: MarkdownProps) => { const style = getStyleSheet(theme); @@ -171,6 +177,19 @@ const Markdown = ({ ); }; + const renderCheckbox = ({isChecked}: {isChecked: boolean}) => { + return ( + + + {' '} + + ); + }; + const renderCodeBlock = (props: any) => { // These sometimes include a trailing newline const content = props.literal.replace(/\n$/, ''); @@ -463,6 +482,8 @@ const Markdown = ({ table_cell: renderTableCell, mention_highlight: Renderer.forwardChildren, + search_highlight: Renderer.forwardChildren, + checkbox: renderCheckbox, editedIndicator: renderEditedIndicator, }; @@ -471,6 +492,7 @@ const Markdown = ({ renderers, renderParagraphsInLists: true, getExtraPropsForNode, + allowedTypes: Object.keys(renderers), }); }; @@ -481,9 +503,13 @@ const Markdown = ({ ast = combineTextNodes(ast); ast = addListItemIndices(ast); ast = pullOutImages(ast); + ast = parseTaskLists(ast); if (mentionKeys) { ast = highlightMentions(ast, mentionKeys); } + if (searchPatterns) { + ast = highlightSearchPatterns(ast, searchPatterns); + } if (isEdited) { const editIndicatorNode = new Node('edited_indicator'); diff --git a/app/components/markdown/transform.test.ts b/app/components/markdown/transform.test.ts index ab487325a..0b5138584 100644 --- a/app/components/markdown/transform.test.ts +++ b/app/components/markdown/transform.test.ts @@ -8,12 +8,15 @@ import {Node, Parser} from 'commonmark'; import { addListItemIndices, combineTextNodes, - getFirstMention, + getFirstMatch, highlightMentions, highlightTextNode, + mentionKeysToPatterns, pullOutImages, } from '@components/markdown/transform'; +import type {UserMentionKey} from '@typings/global/markdown'; + /* eslint-disable max-lines, no-console, no-underscore-dangle */ describe('Components.Markdown.transform', () => { @@ -2623,64 +2626,69 @@ describe('Components.Markdown.transform', () => { name: 'no mention keys', input: 'apple banana orange', mentionKeys: [], - expected: {index: -1, mention: null}, + expected: {index: -1, length: -1}, }, { name: 'single mention', input: 'apple banana orange', mentionKeys: [{key: 'banana'}], - expected: {index: 6, mention: {key: 'banana'}}, + expected: {index: 6, length: 6}, }, { name: 'multiple mentions', input: 'apple banana orange', mentionKeys: [{key: 'apple'}, {key: 'orange'}], - expected: {index: 0, mention: {key: 'apple'}}, + expected: {index: 0, length: 5}, }, { name: 'case sensitive', input: 'apple APPLE Apple aPPle', mentionKeys: [{key: 'Apple', caseSensitive: true}], - expected: {index: 12, mention: {key: 'Apple', caseSensitive: true}}, + expected: {index: 12, length: 5}, }, { name: 'followed by period', input: 'banana.', mentionKeys: [{key: 'banana'}], - expected: {index: 0, mention: {key: 'banana'}}, + expected: {index: 0, length: 6}, }, { name: 'followed by underscores', input: 'banana__', mentionKeys: [{key: 'banana'}], - expected: {index: 0, mention: {key: 'banana'}}, + expected: {index: 0, length: 6}, }, { name: 'in brackets', input: '(banana)', mentionKeys: [{key: 'banana'}], - expected: {index: 1, mention: {key: 'banana'}}, + expected: {index: 1, length: 6}, }, { name: 'following punctuation', input: ':banana', mentionKeys: [{key: 'banana'}], - expected: {index: 1, mention: {key: 'banana'}}, + expected: {index: 1, length: 6}, }, { name: 'not part of another word', input: 'pineapple', mentionKeys: [{key: 'apple'}], - expected: {index: -1, mention: null}, + expected: {index: -1, length: -1}, }, { name: 'no error from weird mention keys', input: 'apple banana orange', mentionKeys: [{key: '*\\3_.'}], - expected: {index: -1, mention: null}, + expected: {index: -1, length: -1}, }, { name: 'no blank mention keys', input: 'apple banana orange', mentionKeys: [{key: ''}], - expected: {index: -1, mention: null}, + expected: {index: -1, length: -1}, }, { name: 'multibyte key', input: '좋은 하루 되세요.', mentionKeys: [{key: '하루'}], - expected: {index: 3, mention: {key: '하루'}}, + expected: {index: 3, length: 2}, }]; + function getFirstMention(str: string, mentionKeys: UserMentionKey[]) { + const patterns = mentionKeysToPatterns(mentionKeys); + return getFirstMatch(str, patterns); + } + for (const test of tests) { it(test.name, () => { const actual = getFirstMention(test.input, test.mentionKeys); diff --git a/app/components/markdown/transform.ts b/app/components/markdown/transform.ts index 18aaa2096..d5ddf5e14 100644 --- a/app/components/markdown/transform.ts +++ b/app/components/markdown/transform.ts @@ -5,7 +5,7 @@ import {Node, NodeType} from 'commonmark'; import {escapeRegex} from '@utils/markdown'; -import type {UserMentionKey} from '@typings/global/markdown'; +import type {SearchPattern, UserMentionKey} from '@typings/global/markdown'; /* eslint-disable no-underscore-dangle */ @@ -114,6 +114,8 @@ function pullOutImage(image: any) { export function highlightMentions(ast: Node, mentionKeys: UserMentionKey[]) { const walker = ast.walker(); + const patterns = mentionKeysToPatterns(mentionKeys); + let e; while ((e = walker.next())) { if (!e.entering) { @@ -123,13 +125,13 @@ export function highlightMentions(ast: Node, mentionKeys: UserMentionKey[]) { const node = e.node; if (node.type === 'text' && node.literal) { - const {index, mention} = getFirstMention(node.literal, mentionKeys); + const {index, length} = getFirstMatch(node.literal, patterns); - if (index === -1 || !mention) { + if (index === -1) { continue; } - const mentionNode = highlightTextNode(node, index, index + mention.key.length, 'mention_highlight'); + const mentionNode = highlightTextNode(node, index, index + length, 'mention_highlight'); // Resume processing on the next node after the mention node which may include any remaining text // that was part of this one @@ -158,38 +160,71 @@ export function highlightMentions(ast: Node, mentionKeys: UserMentionKey[]) { return ast; } -// Given a string and an array of mention keys, returns the first mention that appears and its index. -export function getFirstMention(str: string, mentionKeys: UserMentionKey[]) { - let firstMention = null; - let firstMentionIndex = -1; - - for (const mention of mentionKeys) { - if (mention.key.trim() === '') { - continue; - } - +export function mentionKeysToPatterns(mentionKeys: UserMentionKey[]) { + return mentionKeys.filter((mention) => mention.key.trim() !== '').map((mention) => { const flags = mention.caseSensitive ? '' : 'i'; let pattern; if (cjkPattern.test(mention.key)) { pattern = new RegExp(`${escapeRegex(mention.key)}`, flags); } else { - pattern = new RegExp(`\\b${escapeRegex(mention.key)}_*\\b`, flags); + pattern = new RegExp(`\\b${escapeRegex(mention.key)}(?=_*\\b)`, flags); } + return pattern; + }); +} + +export function highlightSearchPatterns(ast: Node, searchPatterns: SearchPattern[]) { + const walker = ast.walker(); + + let e; + while ((e = walker.next())) { + if (!e.entering) { + continue; + } + + const node = e.node; + + if (node.type === 'text' && node.literal) { + const {index, length} = getFirstMatch(node.literal, searchPatterns.map((pattern) => pattern.pattern)); + + // TODO we might need special handling here for if the search term is part of a hashtag + + if (index === -1) { + continue; + } + + const matchNode = highlightTextNode(node, index, index + length, 'search_highlight'); + + // Resume processing on the next node after the match node which may include any remaining text + // that was part of this one + walker.resumeAt(matchNode, false); + } + } + + return ast; +} + +// Given a string and an array of regexes, returns the index and length of the first match. +export function getFirstMatch(str: string, patterns: RegExp[]) { + let firstMatchIndex = -1; + let firstMatchLength = -1; + + for (const pattern of patterns) { const match = pattern.exec(str); if (!match || match[0] === '') { continue; } - if (firstMentionIndex === -1 || match.index < firstMentionIndex) { - firstMentionIndex = match.index; - firstMention = mention; + if (firstMatchIndex === -1 || match.index < firstMatchIndex) { + firstMatchIndex = match.index; + firstMatchLength = match[0].length; } } return { - index: firstMentionIndex, - mention: firstMention, + index: firstMatchIndex, + length: firstMatchLength, }; } @@ -252,3 +287,39 @@ function wrapNode(wrapper: any, node: any) { wrapper._lastChild = node; node._parent = wrapper; } + +export function parseTaskLists(ast: Node) { + const walker = ast.walker(); + + let e; + while ((e = walker.next())) { + if (!e.entering) { + continue; + } + + const node = e.node; + + if (node.type !== 'item') { + continue; + } + + if (node.firstChild?.type === 'paragraph' && node.firstChild?.firstChild?.type === 'text') { + const paragraphNode = node.firstChild; + const textNode = node.firstChild.firstChild; + + const literal = textNode.literal ?? ''; + + const match = (/^ {0,3}\[( |x)\]\s/).exec(literal); + if (match) { + const checkbox = new Node('checkbox'); + checkbox.isChecked = match[1] === 'x'; + + paragraphNode.prependChild(checkbox); + + textNode.literal = literal.substring(match[0].length); + } + } + } + + return ast; +} diff --git a/app/components/post_list/post/body/index.tsx b/app/components/post_list/post/body/index.tsx index d440b50ca..c9450e77a 100644 --- a/app/components/post_list/post/body/index.tsx +++ b/app/components/post_list/post/body/index.tsx @@ -19,6 +19,7 @@ import Message from './message'; import Reactions from './reactions'; import type PostModel from '@typings/database/models/servers/post'; +import type {SearchPattern} from '@typings/global/markdown'; type BodyProps = { appsEnabled: boolean; @@ -34,6 +35,7 @@ type BodyProps = { isPostAddChannelMember: boolean; location: string; post: PostModel; + searchPatterns?: SearchPattern[]; showAddReaction?: boolean; theme: Theme; }; @@ -74,7 +76,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => { const Body = ({ appsEnabled, filesCount, hasReactions, highlight, highlightReplyBar, isEphemeral, isFirstReply, isJumboEmoji, isLastReply, isPendingOrFailed, isPostAddChannelMember, - location, post, showAddReaction, theme, + location, post, searchPatterns, showAddReaction, theme, }: BodyProps) => { const style = getStyleSheet(theme); const isEdited = postEdited(post); @@ -147,6 +149,7 @@ const Body = ({ layoutWidth={layoutWidth} location={location} post={post} + searchPatterns={searchPatterns} theme={theme} /> ); diff --git a/app/components/post_list/post/body/message/message.tsx b/app/components/post_list/post/body/message/message.tsx index 8a0bbb5be..350a16dc6 100644 --- a/app/components/post_list/post/body/message/message.tsx +++ b/app/components/post_list/post/body/message/message.tsx @@ -15,6 +15,7 @@ import ShowMoreButton from './show_more_button'; import type PostModel from '@typings/database/models/servers/post'; import type UserModel from '@typings/database/models/servers/user'; +import type {SearchPattern} from '@typings/global/markdown'; type MessageProps = { currentUser: UserModel; @@ -25,6 +26,7 @@ type MessageProps = { layoutWidth?: number; location: string; post: PostModel; + searchPatterns?: SearchPattern[]; theme: Theme; } @@ -50,7 +52,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => { }; }); -const Message = ({currentUser, highlight, isEdited, isPendingOrFailed, isReplyPost, layoutWidth, location, post, theme}: MessageProps) => { +const Message = ({currentUser, highlight, isEdited, isPendingOrFailed, isReplyPost, layoutWidth, location, post, searchPatterns, theme}: MessageProps) => { const [open, setOpen] = useState(false); const [height, setHeight] = useState(); const dimensions = useWindowDimensions(); @@ -94,6 +96,7 @@ const Message = ({currentUser, highlight, isEdited, isPendingOrFailed, isReplyPo textStyles={textStyles} value={post.message} mentionKeys={mentionKeys} + searchPatterns={searchPatterns} theme={theme} /> diff --git a/app/components/post_list/post/post.tsx b/app/components/post_list/post/post.tsx index 7d0b5bc72..9dae96f15 100644 --- a/app/components/post_list/post/post.tsx +++ b/app/components/post_list/post/post.tsx @@ -31,6 +31,7 @@ import UnreadDot from './unread_dot'; import type PostModel from '@typings/database/models/servers/post'; import type ThreadModel from '@typings/database/models/servers/thread'; import type UserModel from '@typings/database/models/servers/user'; +import type {SearchPattern} from '@typings/global/markdown'; type PostProps = { appsEnabled: boolean; @@ -54,6 +55,7 @@ type PostProps = { post: PostModel; previousPost?: PostModel; reactionsCount: number; + searchPatterns?: SearchPattern[]; shouldRenderReplyButton?: boolean; showAddReaction?: boolean; skipSavedHeader?: boolean; @@ -104,7 +106,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => { const Post = ({ appsEnabled, canDelete, currentUser, differentThreadSequence, filesCount, hasReplies, highlight, highlightPinnedOrSaved = true, highlightReplyBar, isCRTEnabled, isConsecutivePost, isEphemeral, isFirstReply, isSaved, isJumboEmoji, isLastReply, isPostAddChannelMember, - location, post, reactionsCount, shouldRenderReplyButton, skipSavedHeader, skipPinnedHeader, showAddReaction = true, style, + location, post, reactionsCount, searchPatterns, shouldRenderReplyButton, skipSavedHeader, skipPinnedHeader, showAddReaction = true, style, testID, thread, previousPost, }: PostProps) => { const pressDetected = useRef(false); @@ -283,6 +285,7 @@ const Post = ({ isPostAddChannelMember={isPostAddChannelMember} location={location} post={post} + searchPatterns={searchPatterns} showAddReaction={showAddReaction} theme={theme} /> diff --git a/app/utils/markdown/index.ts b/app/utils/markdown/index.ts index 1f5720747..865faae08 100644 --- a/app/utils/markdown/index.ts +++ b/app/utils/markdown/index.ts @@ -98,7 +98,10 @@ export const getMarkdownTextStyles = makeStyleSheetFromTheme((theme: Theme) => { fontFamily: 'OpenSans-Bold', }, mention_highlight: { - fontFamily: 'OpenSans', + backgroundColor: theme.mentionHighlightBg, + color: theme.mentionHighlightLink, + }, + search_highlight: { backgroundColor: theme.mentionHighlightBg, color: theme.mentionHighlightLink, }, diff --git a/package-lock.json b/package-lock.json index 42b031da2..ac5ed1288 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,8 +34,8 @@ "@sentry/react-native": "3.4.2", "@stream-io/flat-list-mvcp": "0.10.2", "base-64": "1.0.0", - "commonmark": "github:mattermost/commonmark.js#d1003be97d15414af6c21894125623c45e3f5096", - "commonmark-react-renderer": "github:mattermost/commonmark-react-renderer#4e52e1725c0ef5b1e2ecfe9883220ec36c2eb67d", + "commonmark": "npm:@mattermost/commonmark@0.30.1-0", + "commonmark-react-renderer": "github:mattermost/commonmark-react-renderer#2c660491041f7595f6ce5a05f6dc2e30ca769d3a", "deep-equal": "2.0.5", "deepmerge": "4.2.2", "emoji-regex": "10.1.0", @@ -4021,6 +4021,37 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@mattermost/commonmark": { + "version": "0.30.1-0", + "resolved": "https://registry.npmjs.org/@mattermost/commonmark/-/commonmark-0.30.1-0.tgz", + "integrity": "sha512-0+qW22COfd/BA81TQ05nQMfhmnuRAkv/vwCbs3iFVTEj7mTunYRwWUyb5M8K849V2VXdLdrS8htsBtIHDb2H7g==", + "peer": true, + "dependencies": { + "entities": "~3.0.1", + "mdurl": "~1.0.1", + "minimist": "~1.2.5", + "string.prototype.repeat": "^1.0.0", + "xregexp": "5.1.0" + }, + "bin": { + "commonmark": "bin/commonmark" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@mattermost/commonmark/node_modules/entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", + "peer": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/@mattermost/compass-icons": { "version": "0.1.23", "resolved": "https://registry.npmjs.org/@mattermost/compass-icons/-/compass-icons-0.1.23.tgz", @@ -9441,9 +9472,10 @@ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" }, "node_modules/commonmark": { - "version": "0.30.0", - "resolved": "git+ssh://git@github.com/mattermost/commonmark.js.git#d1003be97d15414af6c21894125623c45e3f5096", - "license": "BSD-2-Clause", + "name": "@mattermost/commonmark", + "version": "0.30.1-0", + "resolved": "https://registry.npmjs.org/@mattermost/commonmark/-/commonmark-0.30.1-0.tgz", + "integrity": "sha512-0+qW22COfd/BA81TQ05nQMfhmnuRAkv/vwCbs3iFVTEj7mTunYRwWUyb5M8K849V2VXdLdrS8htsBtIHDb2H7g==", "dependencies": { "entities": "~3.0.1", "mdurl": "~1.0.1", @@ -9460,7 +9492,9 @@ }, "node_modules/commonmark-react-renderer": { "version": "4.3.5", - "resolved": "git+ssh://git@github.com/mattermost/commonmark-react-renderer.git#4e52e1725c0ef5b1e2ecfe9883220ec36c2eb67d", + "resolved": "git+ssh://git@github.com/mattermost/commonmark-react-renderer.git#2c660491041f7595f6ce5a05f6dc2e30ca769d3a", + "integrity": "sha512-D++d6UFNLyu4fAAZg6blRb3rkiG+bFWKJ8fSLOJwzBit3Hm/sPZbx2QQeKfx+IHoMg7cCvHlesAT1+kB/+kBLQ==", + "license": "MIT", "dependencies": { "lodash.assign": "^4.2.0", "lodash.isplainobject": "^4.0.6", @@ -9468,7 +9502,7 @@ "xss-filters": "^1.2.6" }, "peerDependencies": { - "commonmark": "^0.30.0", + "@mattermost/commonmark": "*", "react": ">=0.14.0" } }, @@ -28684,6 +28718,27 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "@mattermost/commonmark": { + "version": "0.30.1-0", + "resolved": "https://registry.npmjs.org/@mattermost/commonmark/-/commonmark-0.30.1-0.tgz", + "integrity": "sha512-0+qW22COfd/BA81TQ05nQMfhmnuRAkv/vwCbs3iFVTEj7mTunYRwWUyb5M8K849V2VXdLdrS8htsBtIHDb2H7g==", + "peer": true, + "requires": { + "entities": "~3.0.1", + "mdurl": "~1.0.1", + "minimist": "~1.2.5", + "string.prototype.repeat": "^1.0.0", + "xregexp": "5.1.0" + }, + "dependencies": { + "entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", + "peer": true + } + } + }, "@mattermost/compass-icons": { "version": "0.1.23", "resolved": "https://registry.npmjs.org/@mattermost/compass-icons/-/compass-icons-0.1.23.tgz", @@ -32868,8 +32923,9 @@ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" }, "commonmark": { - "version": "git+ssh://git@github.com/mattermost/commonmark.js.git#d1003be97d15414af6c21894125623c45e3f5096", - "from": "commonmark@github:mattermost/commonmark.js#d1003be97d15414af6c21894125623c45e3f5096", + "version": "npm:@mattermost/commonmark@0.30.1-0", + "resolved": "https://registry.npmjs.org/@mattermost/commonmark/-/commonmark-0.30.1-0.tgz", + "integrity": "sha512-0+qW22COfd/BA81TQ05nQMfhmnuRAkv/vwCbs3iFVTEj7mTunYRwWUyb5M8K849V2VXdLdrS8htsBtIHDb2H7g==", "requires": { "entities": "~3.0.1", "mdurl": "~1.0.1", @@ -32886,8 +32942,9 @@ } }, "commonmark-react-renderer": { - "version": "git+ssh://git@github.com/mattermost/commonmark-react-renderer.git#4e52e1725c0ef5b1e2ecfe9883220ec36c2eb67d", - "from": "commonmark-react-renderer@github:mattermost/commonmark-react-renderer#4e52e1725c0ef5b1e2ecfe9883220ec36c2eb67d", + "version": "git+ssh://git@github.com/mattermost/commonmark-react-renderer.git#2c660491041f7595f6ce5a05f6dc2e30ca769d3a", + "integrity": "sha512-D++d6UFNLyu4fAAZg6blRb3rkiG+bFWKJ8fSLOJwzBit3Hm/sPZbx2QQeKfx+IHoMg7cCvHlesAT1+kB/+kBLQ==", + "from": "commonmark-react-renderer@github:mattermost/commonmark-react-renderer#2c660491041f7595f6ce5a05f6dc2e30ca769d3a", "requires": { "lodash.assign": "^4.2.0", "lodash.isplainobject": "^4.0.6", diff --git a/package.json b/package.json index 266bb04ab..713ecec65 100644 --- a/package.json +++ b/package.json @@ -31,8 +31,8 @@ "@sentry/react-native": "3.4.2", "@stream-io/flat-list-mvcp": "0.10.2", "base-64": "1.0.0", - "commonmark": "github:mattermost/commonmark.js#d1003be97d15414af6c21894125623c45e3f5096", - "commonmark-react-renderer": "github:mattermost/commonmark-react-renderer#4e52e1725c0ef5b1e2ecfe9883220ec36c2eb67d", + "commonmark": "npm:@mattermost/commonmark@0.30.1-0", + "commonmark-react-renderer": "github:mattermost/commonmark-react-renderer#2c660491041f7595f6ce5a05f6dc2e30ca769d3a", "deep-equal": "2.0.5", "deepmerge": "4.2.2", "emoji-regex": "10.1.0", diff --git a/patches/@types+commonmark+0.27.5.patch b/patches/@types+commonmark+0.27.5.patch deleted file mode 100644 index 87b658f35..000000000 --- a/patches/@types+commonmark+0.27.5.patch +++ /dev/null @@ -1,33 +0,0 @@ -diff --git a/node_modules/@types/commonmark/index.d.ts b/node_modules/@types/commonmark/index.d.ts -index 35e9ed6..382cf98 100755 ---- a/node_modules/@types/commonmark/index.d.ts -+++ b/node_modules/@types/commonmark/index.d.ts -@@ -5,8 +5,8 @@ - // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped - - export type NodeType = -- 'text' |'softbreak' | 'linebreak' | 'emph' | 'strong' | 'html_inline' | 'link' | 'image' | 'code' | 'document' | 'paragraph' | -- 'block_quote' | 'item' | 'list' | 'heading' | 'code_block' | 'html_block' | 'thematic_break' | 'custom_inline' | 'custom_block'; -+ 'text' |'softbreak' | 'linebreak' | 'emph' | 'strong' | 'html_inline' | 'link' | 'image' | 'code' | 'document' | 'paragraph' | 'mention_highlight' | 'at_mention' | -+ 'block_quote' | 'item' | 'list' | 'heading' | 'code_block' | 'html_block' | 'thematic_break' | 'custom_inline' | 'custom_block' | 'table' | 'edited_indicator'; - - export class Node { - constructor(nodeType: NodeType, sourcepos?: Position); -@@ -125,6 +125,8 @@ export class Node { - * https://github.com/jgm/commonmark.js/issues/74 - */ - _listData: ListData; -+ -+ mentionName?: string; - } - - /** -@@ -200,6 +202,8 @@ export interface ParserOptions { - */ - smart?: boolean | undefined; - time?: boolean | undefined; -+ urlFilter?: (url: string) => boolean; -+ minimumHashtagLength?: number; - } - - export interface HtmlRenderingOptions extends XmlRenderingOptions { diff --git a/types/global/markdown.ts b/types/global/markdown.ts index f76b89d9f..a478b1128 100644 --- a/types/global/markdown.ts +++ b/types/global/markdown.ts @@ -3,7 +3,12 @@ import {TextStyle, ViewStyle} from 'react-native'; -export type UserMentionKey= { +export type SearchPattern = { + pattern: RegExp; + term: string; +}; + +export type UserMentionKey = { key: string; caseSensitive?: boolean; };