Skip to content

Commit 5f5b8de

Browse files
authored
Merge pull request #793 from GetStream/CRS-441_message-mentions
CRS-441 customizable message mentions
2 parents 1e2b96f + a5816ba commit 5f5b8de

File tree

4 files changed

+55
-41
lines changed

4 files changed

+55
-41
lines changed

src/styles/Message.scss

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -444,12 +444,6 @@
444444
}
445445

446446
&-mention {
447-
margin: 0;
448-
padding: 0;
449-
background: none;
450-
border: none;
451-
font-size: 16px;
452-
color: $primary-color;
453447
font-weight: $heavy-font-weight;
454448
}
455449

src/styles/MessageCommerce.scss

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -183,16 +183,6 @@
183183
text-decoration: none;
184184
}
185185

186-
&-mention {
187-
margin: 0;
188-
padding: 0;
189-
background: none;
190-
border: none;
191-
font-size: 16px;
192-
color: $primary-color;
193-
font-weight: $heavy-font-weight;
194-
}
195-
196186
&--inner {
197187
display: flex;
198188
flex-direction: column;

src/styles/MessageLivestream.scss

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,6 @@
3333
margin: 0;
3434
}
3535

36-
.str-chat__message-mention {
37-
font-size: 13px;
38-
line-height: 20px;
39-
margin: 0;
40-
41-
&:focus {
42-
outline: 1;
43-
}
44-
}
45-
4636
p {
4737
margin: 0;
4838
white-space: pre-line;

src/utils.tsx

Lines changed: 55 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { PropsWithChildren } from 'react';
22
import emojiRegex from 'emoji-regex';
33
import * as linkify from 'linkifyjs';
44
//@ts-expect-error
@@ -107,9 +107,53 @@ const emojiMarkdownPlugin = () => {
107107
return transform;
108108
};
109109

110+
const mentionsMarkdownPlugin = <
111+
Us extends DefaultUserType<Us> = DefaultUserType
112+
>(
113+
mentioned_users: UserResponse<Us>[],
114+
) => () => {
115+
const mentioned_usernames = mentioned_users
116+
.map((user) => user.name || user.id)
117+
.filter(Boolean)
118+
.map(escapeRegExp);
119+
120+
const mentionedUsersRegex = new RegExp(
121+
mentioned_usernames.map((username) => `@${username}`).join('|'),
122+
'g',
123+
);
124+
125+
function replace(match: string) {
126+
const usernameOrId = match.replace('@', '');
127+
const user = mentioned_users.find(
128+
({ id, name }) => name === usernameOrId || id === usernameOrId,
129+
);
130+
return {
131+
children: [{ type: 'text', value: match }],
132+
mentioned_user: user,
133+
type: 'mention',
134+
};
135+
}
136+
137+
const transform = <T extends unknown>(markdownAST: T) => {
138+
findAndReplace(markdownAST, mentionedUsersRegex, replace);
139+
return markdownAST;
140+
};
141+
142+
return transform;
143+
};
144+
145+
type MentionProps<Us extends DefaultUserType<Us> = DefaultUserType> = {
146+
mentioned_user: UserResponse<Us>;
147+
};
148+
149+
const Mention = <Us extends DefaultUserType<Us> = DefaultUserType>(
150+
props: PropsWithChildren<Us>,
151+
) => <span className='str-chat__message-mention'>{props.children}</span>;
152+
110153
export const renderText = <Us extends DefaultUserType<Us> = DefaultUserType>(
111154
text?: string,
112155
mentioned_users?: UserResponse<Us>[],
156+
MentionComponent: React.ComponentType<MentionProps<Us>> = Mention,
113157
) => {
114158
// take the @ mentions and turn them into markdown?
115159
// translate links
@@ -146,27 +190,23 @@ export const renderText = <Us extends DefaultUserType<Us> = DefaultUserType>(
146190
newText = newText.replace(value, `[${displayLink}](${encodeURI(href)})`);
147191
});
148192

149-
if (mentioned_users && mentioned_users.length) {
150-
for (let i = 0; i < mentioned_users.length; i++) {
151-
let username = mentioned_users[i].name || mentioned_users[i].id;
193+
const plugins = [emojiMarkdownPlugin];
152194

153-
if (username) {
154-
username = escapeRegExp(username);
155-
}
156-
157-
const nameMarkdown = `**@${username}**`;
158-
const nameRegex = new RegExp(`@${username}`, 'g');
159-
160-
newText = newText.replace(nameRegex, nameMarkdown);
161-
}
195+
if (mentioned_users?.length) {
196+
plugins.push(mentionsMarkdownPlugin(mentioned_users));
162197
}
163198

199+
const renderers = {
200+
...markDownRenderers,
201+
mention: MentionComponent,
202+
};
203+
164204
return (
165205
<ReactMarkdown
166206
allowedTypes={allowedMarkups}
167207
escapeHtml={true}
168-
plugins={[emojiMarkdownPlugin]}
169-
renderers={markDownRenderers}
208+
plugins={plugins}
209+
renderers={renderers}
170210
source={newText}
171211
transformLinkUri={(uri) =>
172212
uri.startsWith('app://') ? uri : RootReactMarkdown.uriTransformer(uri)

0 commit comments

Comments
 (0)