Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"lodash.isequal": "^4.5.0",
"lodash.throttle": "^4.1.1",
"lodash.uniqby": "^4.7.0",
"mdast-util-find-and-replace": "1.1.1",
"mdast-util-find-and-replace": "^2.2.1",
"nanoid": "^3.3.4",
"pretty-bytes": "^5.4.1",
"prop-types": "^15.7.2",
Expand Down Expand Up @@ -103,6 +103,7 @@
"@types/lodash.isequal": "^4.5.5",
"@types/lodash.throttle": "^4.1.6",
"@types/lodash.uniqby": "^4.7.6",
"@types/mdast": "^3.0.10",
"@types/moment": "^2.13.0",
"@types/react": "^18.0.8",
"@types/react-dom": "^18.0.3",
Expand Down Expand Up @@ -208,7 +209,8 @@
"e2e-container": "./e2e/scripts/run_in_container.sh"
},
"resolutions": {
"ast-types": "^0.14.0"
"ast-types": "^0.14.0",
"@types/unist": "^2.0.6"
},
"browserslist": [
">0.2%",
Expand Down
142 changes: 142 additions & 0 deletions src/__tests__/__snapshots__/utils.test.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`renderText handles the special case where user name matches to an e-mail pattern - 1 1`] = `
<p>
Hello
<span
className="str-chat__message-mention"
>
@username@email.com
</span>
, is
<a
className=""
href="mailto:username@email.com"
rel="nofollow noreferrer noopener"
target="_blank"
>
username@email.com
</a>
your @primary e-mail?
</p>
`;

exports[`renderText handles the special case where user name matches to an e-mail pattern - 2 1`] = `
<p>
<a
className=""
href="mailto:username@email.com"
rel="nofollow noreferrer noopener"
target="_blank"
>
username@email.com
</a>

<span
className="str-chat__message-mention"
>
@username@email.com
</span>
is this the right address?
</p>
`;

exports[`renderText handles the special case where user name matches to an e-mail pattern - 3 1`] = `
<p>
<span
className="str-chat__message-mention"
>
@username@email.com
</span>

<span
className="str-chat__message-mention"
>
@username@email.com
</span>

<span
className="str-chat__message-mention"
>
@username@email.com
</span>

<span
className="str-chat__message-mention"
>
@username@email.com
</span>
</p>
`;

exports[`renderText handles the special case where user name matches to an e-mail pattern - 4 1`] = `
<p>
<span
className="str-chat__message-mention"
>
@username@email.com
</span>

<span
className="str-chat__message-mention"
>
@username@email.com
</span>

<a
className=""
href="mailto:username@email.com"
rel="nofollow noreferrer noopener"
target="_blank"
>
username@email.com
</a>

<span
className="str-chat__message-mention"
>
@username@email.com
</span>
</p>
`;

exports[`renderText renders custom mention 1`] = `
<p>
<span
className="my-mention"
>
@username@email.com
</span>

<span
className="my-mention"
>
@username@email.com
</span>

<a
className=""
href="mailto:username@email.com"
rel="nofollow noreferrer noopener"
target="_blank"
>
username@email.com
</a>

<span
className="my-mention"
>
@username@email.com
</span>
</p>
`;

exports[`renderText renders standard markdown text 1`] = `
<p>
Hi, shall we meet on
<strong>
Tuesday
</strong>
?
</p>
`;
63 changes: 63 additions & 0 deletions src/__tests__/utils.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React from 'react';
import renderer from 'react-test-renderer';
import { renderText } from '../utils';

describe(`renderText`, () => {
it('handles the special case where user name matches to an e-mail pattern - 1', () => {
const Markdown = renderText(
'Hello @username@email.com, is username@email.com your @primary e-mail?',
[{ id: 'id-username@email.com', name: 'username@email.com' }],
);
const tree = renderer.create(Markdown).toJSON();
expect(tree).toMatchSnapshot();
});

it('handles the special case where user name matches to an e-mail pattern - 2', () => {
const Markdown = renderText(
'username@email.com @username@email.com is this the right address?',
[{ id: 'id-username@email.com', name: 'username@email.com' }],
);
const tree = renderer.create(Markdown).toJSON();
expect(tree).toMatchSnapshot();
});

it('handles the special case where user name matches to an e-mail pattern - 3', () => {
const Markdown = renderText(
'@username@email.com @username@email.com @username@email.com @username@email.com',
[{ id: 'id-username@email.com', name: 'username@email.com' }],
);
const tree = renderer.create(Markdown).toJSON();
expect(tree).toMatchSnapshot();
});

it('handles the special case where user name matches to an e-mail pattern - 4', () => {
const Markdown = renderText(
'@username@email.com @username@email.com username@email.com @username@email.com',
[{ id: 'id-username@email.com', name: 'username@email.com' }],
);
const tree = renderer.create(Markdown).toJSON();
expect(tree).toMatchSnapshot();
});

it('renders custom mention', () => {
const Markdown = renderText(
'@username@email.com @username@email.com username@email.com @username@email.com',
[{ id: 'id-username@email.com', name: 'username@email.com' }],
{
customMarkDownRenderers: {
mention: function MyMention(props) {
return <span className='my-mention'>{props.children}</span>;
},
},
},
);
const tree = renderer.create(Markdown).toJSON();
expect(tree).toMatchSnapshot();
});

it('renders standard markdown text', () => {
const Markdown = renderText('Hi, shall we meet on **Tuesday**?', []);
const tree = renderer.create(Markdown).toJSON();
expect(tree).toMatchSnapshot();
});
});
27 changes: 22 additions & 5 deletions src/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@ import React, { PropsWithChildren } from 'react';
import emojiRegex from 'emoji-regex';
import * as linkify from 'linkifyjs';
import { nanoid } from 'nanoid';
//@ts-expect-error
import findAndReplace from 'mdast-util-find-and-replace';
import { findAndReplace, ReplaceFunction } from 'mdast-util-find-and-replace';
import RootReactMarkdown, { NodeType } from 'react-markdown';
import ReactMarkdown from 'react-markdown/with-html';
import uniqBy from 'lodash.uniqby';

import type { UserResponse } from 'stream-chat';

import type { Root } from 'mdast';
import type { DefaultStreamChatGenerics } from './types/types';

export const isOnlyEmojis = (text?: string) => {
Expand Down Expand Up @@ -119,7 +118,7 @@ export const emojiMarkdownPlugin = () => {
}

const transform = <T extends unknown>(markdownAST: T) => {
findAndReplace(markdownAST, emojiRegex(), replace);
findAndReplace(markdownAST as Root, emojiRegex(), replace as ReplaceFunction);
return markdownAST;
};

Expand Down Expand Up @@ -156,7 +155,7 @@ export const mentionsMarkdownPlugin = <
mentioned_usernames.map((username) => `@${username}`).join('|'),
'g',
);
findAndReplace(markdownAST, mentionedUsersRegex, replace);
findAndReplace(markdownAST as Root, mentionedUsersRegex, replace as ReplaceFunction);
return markdownAST;
};

Expand Down Expand Up @@ -213,6 +212,24 @@ export const renderText = <
if (noParsingNeeded.length > 0 || linkIsInBlock) return;

try {
// special case for mentions:
// it could happen that a user's name matches with an e-mail format pattern.
// in that case, we check whether the found e-mail is actually a mention
// by naively checking for an existence of @ sign in front of it.
if (type === 'email' && mentioned_users) {
const emailMatchesWithName = mentioned_users.some((u) => u.name === value);
if (emailMatchesWithName) {
newText = newText.replace(new RegExp(escapeRegExp(value), 'g'), (match, position) => {
const isMention = newText.charAt(position - 1) === '@';
// in case of mention, we leave the match in its original form,
// and we let `mentionsMarkdownPlugin` to do its job
return isMention ? match : `[${match}](${encodeDecode(href)})`;
});

return;
}
}

const displayLink = type === 'email' ? value : formatUrlForDisplay(href);

newText = newText.replace(
Expand Down
51 changes: 32 additions & 19 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3207,10 +3207,10 @@
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.168.tgz#fe24632e79b7ade3f132891afff86caa5e5ce008"
integrity sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q==

"@types/mdast@^3.0.0", "@types/mdast@^3.0.3":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.3.tgz#2d7d671b1cd1ea3deb306ea75036c2a0407d2deb"
integrity sha512-SXPBMnFVQg1s00dlMCc/jCdvPqdE4mXaMMCeRlxLDmTAEoegHT53xKtkDnzDTOcmMHUfcjyf36/YYZ6SxRdnsw==
"@types/mdast@^3.0.0", "@types/mdast@^3.0.3", "@types/mdast@^3.0.10":
version "3.0.10"
resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.10.tgz#4724244a82a4598884cbbe9bcfd73dff927ee8af"
integrity sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==
dependencies:
"@types/unist" "*"

Expand Down Expand Up @@ -3342,10 +3342,10 @@
resolved "https://registry.yarnpkg.com/@types/textarea-caret/-/textarea-caret-3.0.0.tgz#4c5c5e3de5c59511f93ffe929e5383471b828896"
integrity sha512-RNXko6Kl+oQibqxuQZJZ+RgsQAGez4VQxeC4zq+GXjUlHcfjy5EthnsPIQXC5wUSRf5WbyA+4+//mVSc6XwxNw==

"@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2", "@types/unist@^2.0.3":
version "2.0.3"
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e"
integrity sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ==
"@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2", "@types/unist@^2.0.3", "@types/unist@^2.0.6":
version "2.0.6"
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d"
integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==

"@types/uuid@^8.3.0":
version "8.3.0"
Expand Down Expand Up @@ -7481,10 +7481,10 @@ escape-string-regexp@^2.0.0:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344"
integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==

escape-string-regexp@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
escape-string-regexp@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8"
integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==

escodegen@^2.0.0:
version "2.0.0"
Expand Down Expand Up @@ -11846,14 +11846,14 @@ mdast-add-list-metadata@1.0.1:
dependencies:
unist-util-visit-parents "1.1.2"

mdast-util-find-and-replace@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/mdast-util-find-and-replace/-/mdast-util-find-and-replace-1.1.1.tgz#b7db1e873f96f66588c321f1363069abf607d1b5"
integrity sha512-9cKl33Y21lyckGzpSmEQnIDjEfeeWelN5s1kUW1LwdB0Fkuq2u+4GdqcGEygYxJE8GVqCl0741bYXHgamfWAZA==
mdast-util-find-and-replace@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/mdast-util-find-and-replace/-/mdast-util-find-and-replace-2.2.1.tgz#249901ef43c5f41d6e8a8d446b3b63b17e592d7c"
integrity sha512-SobxkQXFAdd4b5WmEakmkVoh18icjQRxGy5OWTCzgsLRm1Fu/KCtwD1HIQSsmq5ZRjVH0Ehwg6/Fn3xIUk+nKw==
dependencies:
escape-string-regexp "^4.0.0"
unist-util-is "^4.0.0"
unist-util-visit-parents "^3.0.0"
escape-string-regexp "^5.0.0"
unist-util-is "^5.0.0"
unist-util-visit-parents "^5.0.0"

mdast-util-from-markdown@^0.8.0:
version "0.8.5"
Expand Down Expand Up @@ -17114,6 +17114,11 @@ unist-util-is@^4.0.0:
resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.1.0.tgz#976e5f462a7a5de73d94b706bac1b90671b57797"
integrity sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==

unist-util-is@^5.0.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-5.1.1.tgz#e8aece0b102fa9bc097b0fef8f870c496d4a6236"
integrity sha512-F5CZ68eYzuSvJjGhCLPL3cYx45IxkqXSetCcRgUXtbcm50X2L9oOWQlfUfDdAf+6Pd27YDblBfdtmsThXmwpbQ==

unist-util-remove-position@^1.0.0:
version "1.1.4"
resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-1.1.4.tgz#ec037348b6102c897703eee6d0294ca4755a2020"
Expand Down Expand Up @@ -17153,6 +17158,14 @@ unist-util-visit-parents@^3.0.0:
"@types/unist" "^2.0.0"
unist-util-is "^4.0.0"

unist-util-visit-parents@^5.0.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-5.1.0.tgz#44bbc5d25f2411e7dfc5cecff12de43296aa8521"
integrity sha512-y+QVLcY5eR/YVpqDsLf/xh9R3Q2Y4HxkZTp7ViLDU6WtJCEcPmRzW1gpdWDCDIqIlhuPDXOgttqPlykrHYDekg==
dependencies:
"@types/unist" "^2.0.0"
unist-util-is "^5.0.0"

unist-util-visit@^1.1.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-1.4.1.tgz#4724aaa8486e6ee6e26d7ff3c8685960d560b1e3"
Expand Down