Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MM-53810] Add mobile markdown support for highlighting without notifications without configuration ability #7663

Merged
merged 18 commits into from
Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions app/components/markdown/markdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ import MarkdownTable from './markdown_table';
import MarkdownTableCell, {type MarkdownTableCellProps} from './markdown_table_cell';
import MarkdownTableImage from './markdown_table_image';
import MarkdownTableRow, {type MarkdownTableRowProps} from './markdown_table_row';
import {addListItemIndices, combineTextNodes, highlightMentions, highlightSearchPatterns, parseTaskLists, pullOutImages} from './transform';
import {addListItemIndices, combineTextNodes, highlightMentions, highlightWithoutNotification, highlightSearchPatterns, parseTaskLists, pullOutImages} from './transform';

import type {
MarkdownAtMentionRenderer, MarkdownBaseRenderer, MarkdownBlockStyles, MarkdownChannelMentionRenderer,
MarkdownEmojiRenderer, MarkdownImageRenderer, MarkdownLatexRenderer, MarkdownTextStyles, SearchPattern, UserMentionKey,
MarkdownEmojiRenderer, MarkdownImageRenderer, MarkdownLatexRenderer, MarkdownTextStyles, SearchPattern, UserMentionKey, HighlightWithoutNotificationKey,
} from '@typings/global/markdown';

type MarkdownProps = {
Expand All @@ -55,6 +55,7 @@ type MarkdownProps = {
disableTables?: boolean;
enableLatex: boolean;
enableInlineLatex: boolean;
highlightKeys?: HighlightWithoutNotificationKey[];
imagesMetadata?: Record<string, PostImage | undefined>;
isEdited?: boolean;
isReplyPost?: boolean;
Expand Down Expand Up @@ -133,7 +134,7 @@ const Markdown = ({
disableCodeBlock, disableGallery, disableHashtags, disableHeading, disableTables,
enableInlineLatex, enableLatex, maxNodes,
imagesMetadata, isEdited, isReplyPost, isSearchResult, layoutHeight, layoutWidth,
location, mentionKeys, minimumHashtagLength = 3, onPostPress, postId, searchPatterns,
location, mentionKeys, highlightKeys, minimumHashtagLength = 3, onPostPress, postId, searchPatterns,
textStyles = {}, theme, value = '', baseParagraphStyle, onLinkLongPress,
}: MarkdownProps) => {
const style = getStyleSheet(theme);
Expand Down Expand Up @@ -578,6 +579,7 @@ const Markdown = ({

mention_highlight: Renderer.forwardChildren,
search_highlight: Renderer.forwardChildren,
highlight_without_notification: Renderer.forwardChildren,
checkbox: renderCheckbox,

editedIndicator: renderEditedIndicator,
Expand All @@ -604,10 +606,12 @@ const Markdown = ({
if (mentionKeys) {
ast = highlightMentions(ast, mentionKeys);
}
if (highlightKeys) {
ast = highlightWithoutNotification(ast, highlightKeys);
}
if (searchPatterns) {
ast = highlightSearchPatterns(ast, searchPatterns);
}

if (isEdited) {
const editIndicatorNode = new Node('edited_indicator');
if (ast.lastChild && ['heading', 'paragraph'].includes(ast.lastChild.type)) {
Expand Down
314 changes: 313 additions & 1 deletion app/components/markdown/transform.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
highlightTextNode,
mentionKeysToPatterns,
pullOutImages,
highlightWithoutNotification,
highlightKeysToPatterns,
} from '@components/markdown/transform';
import {logError} from '@utils/log';

Expand Down Expand Up @@ -2602,7 +2604,7 @@ describe('Components.Markdown.transform', () => {
}
});

describe('getFirstMention', () => {
describe('getFirstMention with mentionKeysToPatterns', () => {
const tests = [{
name: 'no mention keys',
input: 'apple banana orange',
Expand Down Expand Up @@ -2801,6 +2803,316 @@ describe('Components.Markdown.transform', () => {
});
}
});

describe('highlightWithoutNotification', () => {
M-ZubairAhmed marked this conversation as resolved.
Show resolved Hide resolved
const tests = [{
name: 'no highlights',
input: 'Cant put down an anti gravity book',
highlightKeys: [],
expected: {
type: 'document',
children: [{
type: 'paragraph',
children: [{
type: 'text',
literal: 'Cant put down an anti gravity book',
}],
}],
},
}, {
name: 'a word highlight',
input: 'Cant put down an anti gravity book',
highlightKeys: [{key: 'anti'}],
expected: {
type: 'document',
children: [{
type: 'paragraph',
children: [
{
type: 'text',
literal: 'Cant put down an ',
},
{
type: 'highlight_without_notification',
children: [{
type: 'text',
literal: 'anti',
}],
},
{
type: 'text',
literal: ' gravity book',
},
],
}],
},
}, {
name: 'a sentence highlight',
input: 'Cant put down an anti gravity book',
highlightKeys: [{key: 'anti gravity'}],
expected: {
type: 'document',
children: [{
type: 'paragraph',
children: [
{
type: 'text',
literal: 'Cant put down an ',
},
{
type: 'highlight_without_notification',
children: [{
type: 'text',
literal: 'anti gravity',
}],
},
{
type: 'text',
literal: ' book',
},
],
}],
},
}, {
name: 'insensitive keywords',
input: 'Cant put down an anti gravity book',
highlightKeys: [{key: 'dOwN'}, {key: 'Anti'}, {key: 'BOOK'}],
expected: {
type: 'document',
children: [{
type: 'paragraph',
children: [
{
type: 'text',
literal: 'Cant put ',
},
{
type: 'highlight_without_notification',
children: [{
type: 'text',
literal: 'down',
}],
},
{
type: 'text',
literal: ' an ',
},
{
type: 'highlight_without_notification',
children: [{
type: 'text',
literal: 'anti',
}],
},
{
type: 'text',
literal: ' gravity ',
},
{
type: 'highlight_without_notification',
children: [{
type: 'text',
literal: 'book',
}],
},
],
}],
},
}, {
name: 'insensitive keywords',
input: 'Cant put down an anti gravity book',
highlightKeys: [{key: 'dOwN'}, {key: 'Anti'}, {key: 'BOOK'}],
expected: {
type: 'document',
children: [{
type: 'paragraph',
children: [
{
type: 'text',
literal: 'Cant put ',
},
{
type: 'highlight_without_notification',
children: [{
type: 'text',
literal: 'down',
}],
},
{
type: 'text',
literal: ' an ',
},
{
type: 'highlight_without_notification',
children: [{
type: 'text',
literal: 'anti',
}],
},
{
type: 'text',
literal: ' gravity ',
},
{
type: 'highlight_without_notification',
children: [{
type: 'text',
literal: 'book',
}],
},
],
}],
},
}, {
name: 'words with characters surrounding them',
input: 'peace& ^peace -peace-',
highlightKeys: [{key: 'PEACE'}],
expected: {
type: 'document',
children: [{
type: 'paragraph',
children: [
{
type: 'highlight_without_notification',
children: [{
type: 'text',
literal: 'peace',
}],
},
{
type: 'text',
literal: '& ^',
},
{
type: 'highlight_without_notification',
children: [{
type: 'text',
literal: 'peace',
}],
},
{
type: 'text',
literal: ' -',
},
{
type: 'highlight_without_notification',
children: [{
type: 'text',
literal: 'peace',
}],
},
{
type: 'text',
literal: '-',
},
],
}],
},
}, {
name: 'CJK word highlight',
input: '我确实喜欢我的同事。',
highlightKeys: [{key: '喜欢'}],
expected: {
type: 'document',
children: [{
type: 'paragraph',
children: [
{
type: 'text',
literal: '我确实',
},
{
type: 'highlight_without_notification',
children: [{
type: 'text',
literal: '喜欢',
}],
},
{
type: 'text',
literal: '我的同事。',
},
],
}],
},
}];

for (const test of tests) {
it(test.name, () => {
const input = combineTextNodes(parser.parse(test.input));
const expected = makeAst(test.expected);
const actual = highlightWithoutNotification(input, test.highlightKeys);

assert.ok(verifyAst(actual));
assert.deepStrictEqual(stripUnusedFields(actual), stripUnusedFields(expected));
});
}
});

describe('getFirstMatch with highlightKeysToPatterns', () => {
const tests = [{
name: 'text with space before',
input: ' lol',
patterns: highlightKeysToPatterns([{key: 'lol'}]),
expected: {index: 1, length: 3},
},
{
name: 'text with space afterwards and before',
input: ' Lol ',
patterns: highlightKeysToPatterns([{key: 'lol'}]),
expected: {index: 1, length: 3},
},
{
name: 'text with a non word character before (?)',
input: '?Lol',
patterns: highlightKeysToPatterns([{key: 'lol'}]),
expected: {index: 1, length: 3},
},
{
name: 'text with a non word character before (.)',
input: '?Lol',
patterns: highlightKeysToPatterns([{key: 'lol'}]),
expected: {index: 1, length: 3},
},
{
name: 'text with a non word character before (_)',
input: '?Lol',
patterns: highlightKeysToPatterns([{key: 'lol'}]),
expected: {index: 1, length: 3},
},
{
name: 'text with non word character after (.)',
input: 'Lol.',
patterns: highlightKeysToPatterns([{key: 'lol'}]),
expected: {index: 0, length: 3},
},
{
name: 'text with non word character after (?)',
input: 'Lol?',
patterns: highlightKeysToPatterns([{key: 'lol'}]),
expected: {index: 0, length: 3},
},
{
name: 'text with non word character after (_)',
input: 'Lol_',
patterns: highlightKeysToPatterns([{key: 'lol'}]),
expected: {index: 0, length: 3},
},
{
name: 'text with non word character before and after',
input: '?Lol?',
patterns: highlightKeysToPatterns([{key: 'lol'}]),
expected: {index: 1, length: 3},
}];

for (const test of tests) {
it(test.name, () => {
const actual = getFirstMatch(test.input, test.patterns);

assert.deepStrictEqual(actual, test.expected);
});
}
});
});

// Testing and debugging functions
Expand Down
Loading