signal-desktop/ts/components/conversationList/MessageSearchResult.tsx

205 lines
5.2 KiB
TypeScript

// Copyright 2019 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { FunctionComponent, ReactNode } from 'react';
import React, { useCallback } from 'react';
import { noop } from 'lodash';
import { ContactName } from '../conversation/ContactName';
import type { BodyRangesForDisplayType } from '../../types/BodyRange';
import { processBodyRangesForSearchResult } from '../../types/BodyRange';
import type { LocalizerType, ThemeType } from '../../types/Util';
import { BaseConversationListItem } from './BaseConversationListItem';
import type {
ConversationType,
ShowConversationType,
} from '../../state/ducks/conversations';
import type { PreferredBadgeSelectorType } from '../../state/selectors/badges';
import { Intl } from '../Intl';
import {
MessageTextRenderer,
RenderLocation,
} from '../conversation/MessageTextRenderer';
const EMPTY_OBJECT = Object.freeze(Object.create(null));
export type PropsDataType = {
isSelected?: boolean;
isSearchingInConversation?: boolean;
id: string;
conversationId: string;
sentAt?: number;
snippet: string;
body: string;
bodyRanges: BodyRangesForDisplayType;
from: Pick<
ConversationType,
| 'acceptedMessageRequest'
| 'avatarPath'
| 'badges'
| 'color'
| 'isMe'
| 'phoneNumber'
| 'profileName'
| 'sharedGroupNames'
| 'title'
| 'type'
| 'unblurredAvatarPath'
>;
to: Pick<
ConversationType,
'isMe' | 'phoneNumber' | 'profileName' | 'title' | 'type'
>;
};
type PropsHousekeepingType = {
getPreferredBadge: PreferredBadgeSelectorType;
i18n: LocalizerType;
showConversation: ShowConversationType;
theme: ThemeType;
};
export type PropsType = PropsDataType & PropsHousekeepingType;
const renderPerson = (
i18n: LocalizerType,
person: Readonly<{
isMe?: boolean;
title: string;
}>
): ReactNode =>
person.isMe ? i18n('icu:you') : <ContactName title={person.title} />;
export const MessageSearchResult: FunctionComponent<PropsType> = React.memo(
function MessageSearchResult({
body,
bodyRanges,
conversationId,
from,
getPreferredBadge,
i18n,
id,
sentAt,
showConversation,
snippet,
theme,
to,
}) {
const onClickItem = useCallback(() => {
showConversation({ conversationId, messageId: id });
}, [showConversation, conversationId, id]);
if (!from || !to) {
// Note: mapStateToProps() may return null if the message is not found.
return <div />;
}
const isNoteToSelf = from.isMe && to.isMe;
let headerName: ReactNode;
if (isNoteToSelf) {
headerName = i18n('icu:noteToSelf');
} else if (from.isMe) {
if (to.type === 'group') {
headerName = (
<span>
<Intl
i18n={i18n}
id="icu:searchResultHeader--you-to-group"
components={{
receiverGroup: renderPerson(i18n, to),
}}
/>
</span>
);
} else {
headerName = (
<span>
<Intl
i18n={i18n}
id="icu:searchResultHeader--you-to-receiver"
components={{
receiverContact: renderPerson(i18n, to),
}}
/>
</span>
);
}
} else {
// eslint-disable-next-line no-lonely-if
if (to.type === 'group') {
headerName = (
<span>
<Intl
i18n={i18n}
id="icu:searchResultHeader--sender-to-group"
components={{
sender: renderPerson(i18n, from),
receiverGroup: renderPerson(i18n, to),
}}
/>
</span>
);
} else {
headerName = (
<span>
<Intl
i18n={i18n}
id="icu:searchResultHeader--sender-to-you"
components={{
sender: renderPerson(i18n, from),
}}
/>
</span>
);
}
}
const { cleanedSnippet, bodyRanges: displayBodyRanges } =
processBodyRangesForSearchResult({ snippet, body, bodyRanges });
const messageText = (
<MessageTextRenderer
messageText={cleanedSnippet}
bodyRanges={displayBodyRanges}
direction={undefined}
disableLinks
emojiSizeClass={undefined}
i18n={i18n}
isSpoilerExpanded={EMPTY_OBJECT}
onMentionTrigger={noop}
renderLocation={RenderLocation.SearchResult}
textLength={cleanedSnippet.length}
/>
);
return (
<BaseConversationListItem
acceptedMessageRequest={from.acceptedMessageRequest}
avatarPath={from.avatarPath}
badge={getPreferredBadge(from.badges)}
color={from.color}
conversationType="direct"
headerDate={sentAt}
headerName={headerName}
i18n={i18n}
id={id}
isNoteToSelf={isNoteToSelf}
isMe={from.isMe}
isSelected={false}
messageText={messageText}
onClick={onClickItem}
phoneNumber={from.phoneNumber}
profileName={from.profileName}
sharedGroupNames={from.sharedGroupNames}
theme={theme}
title={from.title}
unblurredAvatarPath={from.unblurredAvatarPath}
/>
);
}
);