@mentions receive support
This commit is contained in:
parent
c126a71864
commit
9657c38987
18 changed files with 555 additions and 23 deletions
104
ts/components/conversation/AtMentionify.tsx
Normal file
104
ts/components/conversation/AtMentionify.tsx
Normal file
|
@ -0,0 +1,104 @@
|
|||
import React from 'react';
|
||||
import { Emojify } from './Emojify';
|
||||
import { BodyRangesType } from '../../types/Util';
|
||||
|
||||
export type Props = {
|
||||
bodyRanges?: BodyRangesType;
|
||||
direction?: 'incoming' | 'outgoing';
|
||||
openConversation?: (conversationId: string, messageId?: string) => void;
|
||||
text: string;
|
||||
};
|
||||
|
||||
export const AtMentionify = ({
|
||||
bodyRanges,
|
||||
direction,
|
||||
openConversation,
|
||||
text,
|
||||
}: Props): JSX.Element => {
|
||||
if (!bodyRanges) {
|
||||
return <>{text}</>;
|
||||
}
|
||||
|
||||
const MENTIONS_REGEX = /(\uFFFC@(\d+))/g;
|
||||
|
||||
let match = MENTIONS_REGEX.exec(text);
|
||||
let last = 0;
|
||||
|
||||
const rangeStarts = new Map();
|
||||
bodyRanges.forEach(range => {
|
||||
rangeStarts.set(range.start, range);
|
||||
});
|
||||
|
||||
const results = [];
|
||||
while (match) {
|
||||
if (last < match.index) {
|
||||
const textWithNoMentions = text.slice(last, match.index);
|
||||
results.push(textWithNoMentions);
|
||||
}
|
||||
|
||||
const rangeStart = Number(match[2]);
|
||||
const range = rangeStarts.get(rangeStart);
|
||||
|
||||
if (range) {
|
||||
results.push(
|
||||
<span
|
||||
className={`module-message-body__at-mention module-message-body__at-mention--${direction}`}
|
||||
key={range.start}
|
||||
onClick={() => {
|
||||
if (openConversation && range.conversationID) {
|
||||
openConversation(range.conversationID);
|
||||
}
|
||||
}}
|
||||
onKeyUp={e => {
|
||||
if (
|
||||
e.target === e.currentTarget &&
|
||||
e.keyCode === 13 &&
|
||||
openConversation &&
|
||||
range.conversationID
|
||||
) {
|
||||
openConversation(range.conversationID);
|
||||
}
|
||||
}}
|
||||
tabIndex={0}
|
||||
role="link"
|
||||
>
|
||||
@
|
||||
<Emojify text={range.replacementText} />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
last = MENTIONS_REGEX.lastIndex;
|
||||
match = MENTIONS_REGEX.exec(text);
|
||||
}
|
||||
|
||||
if (last < text.length) {
|
||||
results.push(text.slice(last));
|
||||
}
|
||||
|
||||
return <>{results}</>;
|
||||
};
|
||||
|
||||
// At-mentions need to be pre-processed before being pushed through the
|
||||
// AtMentionify component, this is due to bodyRanges containing start+length
|
||||
// values that operate on the raw string. The text has to be passed through
|
||||
// other components before being rendered in the <MessageBody />, components
|
||||
// such as Linkify, and Emojify. These components receive the text prop as a
|
||||
// string, therefore we're unable to mark it up with DOM nodes prior to handing
|
||||
// it off to them. This function will encode the "start" position into the text
|
||||
// string so we can later pull it off when rendering the @mention.
|
||||
AtMentionify.preprocessMentions = (
|
||||
text: string,
|
||||
bodyRanges?: BodyRangesType
|
||||
): string => {
|
||||
if (!bodyRanges || !bodyRanges.length) {
|
||||
return text;
|
||||
}
|
||||
|
||||
return bodyRanges.reduce((str, range) => {
|
||||
const textBegin = str.substr(0, range.start);
|
||||
const encodedMention = `\uFFFC@${range.start}`;
|
||||
const textEnd = str.substr(range.start + range.length, str.length);
|
||||
return `${textBegin}${encodedMention}${textEnd}`;
|
||||
}, text);
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue