Support for receiving formatted messages
Co-authored-by: Alvaro Carrasco <alvaro@signal.org>
This commit is contained in:
parent
d34d187f1e
commit
d9d820e72a
72 changed files with 3421 additions and 858 deletions
|
@ -6,56 +6,35 @@ import React from 'react';
|
|||
|
||||
import type { AttachmentType } from '../../types/Attachment';
|
||||
import { canBeDownloaded } from '../../types/Attachment';
|
||||
import type { SizeClassType } from '../emoji/lib';
|
||||
import { getSizeClass } from '../emoji/lib';
|
||||
import { AtMentionify } from './AtMentionify';
|
||||
import { Emojify } from './Emojify';
|
||||
import { AddNewLines } from './AddNewLines';
|
||||
import { Linkify } from './Linkify';
|
||||
|
||||
import type { ShowConversationType } from '../../state/ducks/conversations';
|
||||
import type {
|
||||
HydratedBodyRangesType,
|
||||
LocalizerType,
|
||||
RenderTextCallbackType,
|
||||
} from '../../types/Util';
|
||||
import type { HydratedBodyRangesType } from '../../types/BodyRange';
|
||||
import type { LocalizerType } from '../../types/Util';
|
||||
import { MessageTextRenderer } from './MessageTextRenderer';
|
||||
import type { RenderLocation } from './MessageTextRenderer';
|
||||
|
||||
export type Props = {
|
||||
author?: string;
|
||||
bodyRanges?: HydratedBodyRangesType;
|
||||
direction?: 'incoming' | 'outgoing';
|
||||
/** If set, all emoji will be the same size. Otherwise, just one emoji will be large. */
|
||||
// If set, all emoji will be the same size. Otherwise, just one emoji will be large.
|
||||
disableJumbomoji?: boolean;
|
||||
/** If set, links will be left alone instead of turned into clickable `<a>` tags. */
|
||||
// If set, interactive elements will be left as plain text: links, mentions, spoilers
|
||||
disableLinks?: boolean;
|
||||
i18n: LocalizerType;
|
||||
isSpoilerExpanded: boolean;
|
||||
kickOffBodyDownload?: () => void;
|
||||
onExpandSpoiler?: () => unknown;
|
||||
onIncreaseTextLength?: () => unknown;
|
||||
prefix?: string;
|
||||
renderLocation: RenderLocation;
|
||||
showConversation?: ShowConversationType;
|
||||
text: string;
|
||||
textAttachment?: Pick<AttachmentType, 'pending' | 'digest' | 'key'>;
|
||||
};
|
||||
|
||||
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}
|
||||
/>
|
||||
);
|
||||
|
||||
/**
|
||||
* This component makes it very easy to use all three of our message formatting
|
||||
* components: `Emojify`, `Linkify`, and `AddNewLines`. Because each of them is fully
|
||||
|
@ -69,8 +48,12 @@ export function MessageBody({
|
|||
disableJumbomoji,
|
||||
disableLinks,
|
||||
i18n,
|
||||
isSpoilerExpanded,
|
||||
kickOffBodyDownload,
|
||||
onExpandSpoiler,
|
||||
onIncreaseTextLength,
|
||||
prefix,
|
||||
renderLocation,
|
||||
showConversation,
|
||||
text,
|
||||
textAttachment,
|
||||
|
@ -80,31 +63,6 @@ export function MessageBody({
|
|||
textAttachment?.pending || hasReadMore ? `${text}...` : text;
|
||||
|
||||
const sizeClass = disableJumbomoji ? undefined : getSizeClass(text);
|
||||
const processedText = AtMentionify.preprocessMentions(
|
||||
textWithSuffix,
|
||||
bodyRanges
|
||||
);
|
||||
|
||||
const renderNewLines: RenderTextCallbackType = ({
|
||||
text: textWithNewLines,
|
||||
key,
|
||||
}) => {
|
||||
return (
|
||||
<AddNewLines
|
||||
key={key}
|
||||
text={textWithNewLines}
|
||||
renderNonNewLine={({ text: innerText, key: innerKey }) => (
|
||||
<AtMentionify
|
||||
key={innerKey}
|
||||
bodyRanges={bodyRanges}
|
||||
direction={direction}
|
||||
showConversation={showConversation}
|
||||
text={innerText}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
let pendingContent: React.ReactNode;
|
||||
if (hasReadMore) {
|
||||
|
@ -145,39 +103,35 @@ export function MessageBody({
|
|||
{author && (
|
||||
<>
|
||||
<span className="MessageBody__author">
|
||||
{renderEmoji({
|
||||
i18n,
|
||||
text: author,
|
||||
sizeClass,
|
||||
key: 0,
|
||||
renderNonEmoji: renderNewLines,
|
||||
})}
|
||||
<Emojify text={author} />
|
||||
</span>
|
||||
:{' '}
|
||||
</>
|
||||
)}
|
||||
{disableLinks ? (
|
||||
renderEmoji({
|
||||
i18n,
|
||||
text: processedText,
|
||||
sizeClass,
|
||||
key: 0,
|
||||
renderNonEmoji: renderNewLines,
|
||||
})
|
||||
) : (
|
||||
<Linkify
|
||||
text={processedText}
|
||||
renderNonLink={({ key, text: nonLinkText }) => {
|
||||
return renderEmoji({
|
||||
i18n,
|
||||
text: nonLinkText,
|
||||
sizeClass,
|
||||
key,
|
||||
renderNonEmoji: renderNewLines,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{prefix && (
|
||||
<>
|
||||
<span className="MessageBody__prefix">
|
||||
<Emojify text={prefix} />
|
||||
</span>{' '}
|
||||
</>
|
||||
)}
|
||||
|
||||
<MessageTextRenderer
|
||||
bodyRanges={bodyRanges ?? []}
|
||||
direction={direction}
|
||||
disableLinks={disableLinks ?? false}
|
||||
emojiSizeClass={sizeClass}
|
||||
i18n={i18n}
|
||||
isSpoilerExpanded={isSpoilerExpanded}
|
||||
messageText={textWithSuffix}
|
||||
onMentionTrigger={conversationId =>
|
||||
showConversation?.({ conversationId })
|
||||
}
|
||||
onExpandSpoiler={onExpandSpoiler}
|
||||
renderLocation={renderLocation}
|
||||
textLength={text.length}
|
||||
/>
|
||||
|
||||
{pendingContent}
|
||||
{onIncreaseTextLength ? (
|
||||
<button
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue