// Copyright 2019-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import type { ReactNode } from 'react'; import React from 'react'; import { MESSAGE_TEXT_CLASS_NAME } from './BaseConversationListItem'; import { AtMentionify } from '../conversation/AtMentionify'; import { MessageBody } from '../conversation/MessageBody'; import { Emojify } from '../conversation/Emojify'; import { AddNewLines } from '../conversation/AddNewLines'; import type { SizeClassType } from '../emoji/lib'; import type { BodyRangesType, LocalizerType, RenderTextCallbackType, } from '../../types/Util'; const CLASS_NAME = `${MESSAGE_TEXT_CLASS_NAME}__message-search-result-contents`; export type Props = { bodyRanges: BodyRangesType; text: string; i18n: LocalizerType; }; const renderEmoji = ({ text, key, sizeClass, renderNonEmoji, }: { i18n: LocalizerType; text: string; key: number; sizeClass?: SizeClassType; renderNonEmoji: RenderTextCallbackType; }) => ( <Emojify key={key} text={text} sizeClass={sizeClass} renderNonEmoji={renderNonEmoji} /> ); export class MessageBodyHighlight extends React.Component<Props> { private readonly renderNewLines: RenderTextCallbackType = ({ text: textWithNewLines, key, }) => { const { bodyRanges } = this.props; return ( <AddNewLines key={key} text={textWithNewLines} renderNonNewLine={({ text, key: innerKey }) => ( <AtMentionify bodyRanges={bodyRanges} key={innerKey} text={text} /> )} /> ); }; private renderContents(): ReactNode { const { bodyRanges, text, i18n } = this.props; const results: Array<JSX.Element> = []; const FIND_BEGIN_END = /<<left>>(.+?)<<right>>/g; const processedText = AtMentionify.preprocessMentions(text, bodyRanges); let match = FIND_BEGIN_END.exec(processedText); let last = 0; let count = 1; if (!match) { return ( <MessageBody bodyRanges={bodyRanges} disableJumbomoji disableLinks text={text} i18n={i18n} /> ); } const sizeClass = ''; while (match) { if (last < match.index) { const beforeText = processedText.slice(last, match.index); count += 1; results.push( renderEmoji({ text: beforeText, sizeClass, key: count, i18n, renderNonEmoji: this.renderNewLines, }) ); } const [, toHighlight] = match; count += 2; results.push( <span className="MessageBody__highlight" key={count - 1}> {renderEmoji({ text: toHighlight, sizeClass, key: count, i18n, renderNonEmoji: this.renderNewLines, })} </span> ); last = FIND_BEGIN_END.lastIndex; match = FIND_BEGIN_END.exec(processedText); } if (last < processedText.length) { count += 1; results.push( renderEmoji({ text: processedText.slice(last), sizeClass, key: count, i18n, renderNonEmoji: this.renderNewLines, }) ); } return results; } public override render(): ReactNode { return <div className={CLASS_NAME}>{this.renderContents()}</div>; } }