148 lines
4.1 KiB
TypeScript
148 lines
4.1 KiB
TypeScript
import React, {
|
|
FC, memo, useCallback,
|
|
} from '../../../lib/teact/teact';
|
|
import { withGlobal } from '../../../lib/teact/teactn';
|
|
|
|
import { GlobalActions } from '../../../global/types';
|
|
import {
|
|
ApiChat, ApiUser, ApiMessage, ApiMessageOutgoingStatus,
|
|
} from '../../../api/types';
|
|
|
|
import { IS_SINGLE_COLUMN_LAYOUT } from '../../../util/environment';
|
|
import {
|
|
getChatTitle,
|
|
getPrivateChatUserId,
|
|
getMessageMediaHash,
|
|
getMessageSummaryText,
|
|
getMessageMediaThumbDataUri,
|
|
getMessageVideo,
|
|
getMessageRoundVideo,
|
|
} from '../../../modules/helpers';
|
|
import { selectChat, selectUser } from '../../../modules/selectors';
|
|
import renderText from '../../common/helpers/renderText';
|
|
import { pick } from '../../../util/iteratees';
|
|
import useMedia from '../../../hooks/useMedia';
|
|
import { formatPastTimeShort } from '../../../util/dateFormat';
|
|
import useLang, { LangFn } from '../../../hooks/useLang';
|
|
import useSelectWithEnter from '../../../hooks/useSelectWithEnter';
|
|
|
|
import Avatar from '../../common/Avatar';
|
|
import VerifiedIcon from '../../common/VerifiedIcon';
|
|
import ListItem from '../../ui/ListItem';
|
|
import Link from '../../ui/Link';
|
|
|
|
import './ChatMessage.scss';
|
|
|
|
type OwnProps = {
|
|
searchQuery?: string;
|
|
message: ApiMessage;
|
|
chatId: number;
|
|
};
|
|
|
|
type StateProps = {
|
|
chat?: ApiChat;
|
|
privateChatUser?: ApiUser;
|
|
lastMessageOutgoingStatus?: ApiMessageOutgoingStatus;
|
|
lastSyncTime?: number;
|
|
};
|
|
|
|
type DispatchProps = Pick<GlobalActions, 'focusMessage'>;
|
|
|
|
const ChatMessage: FC<OwnProps & StateProps & DispatchProps> = ({
|
|
message,
|
|
searchQuery,
|
|
chatId,
|
|
chat,
|
|
privateChatUser,
|
|
focusMessage,
|
|
lastSyncTime,
|
|
}) => {
|
|
const mediaThumbnail = getMessageMediaThumbDataUri(message);
|
|
const mediaBlobUrl = useMedia(getMessageMediaHash(message, 'micro'));
|
|
const isRoundVideo = Boolean(getMessageRoundVideo(message));
|
|
|
|
const handleClick = useCallback(() => {
|
|
focusMessage({ chatId, messageId: message.id });
|
|
}, [chatId, focusMessage, message.id]);
|
|
|
|
const lang = useLang();
|
|
|
|
const buttonRef = useSelectWithEnter(handleClick);
|
|
|
|
if (!chat) {
|
|
return undefined;
|
|
}
|
|
|
|
return (
|
|
<ListItem
|
|
className="ChatMessage chat-item-clickable"
|
|
ripple={!IS_SINGLE_COLUMN_LAYOUT}
|
|
onClick={handleClick}
|
|
buttonRef={buttonRef}
|
|
>
|
|
<Avatar
|
|
chat={chat}
|
|
user={privateChatUser}
|
|
withOnlineStatus
|
|
isSavedMessages={privateChatUser && privateChatUser.isSelf}
|
|
lastSyncTime={lastSyncTime}
|
|
/>
|
|
<div className="info">
|
|
<div className="info-row">
|
|
<div className="title">
|
|
<h3 dir="auto">{renderText(getChatTitle(lang, chat, privateChatUser))}</h3>
|
|
{chat.isVerified && <VerifiedIcon />}
|
|
</div>
|
|
<div className="message-date">
|
|
<Link className="date">
|
|
{formatPastTimeShort(lang, message.date * 1000)}
|
|
</Link>
|
|
</div>
|
|
|
|
</div>
|
|
<div className="subtitle">
|
|
<div className="message" dir="auto">
|
|
{renderMessageSummary(lang, message, mediaBlobUrl || mediaThumbnail, searchQuery, isRoundVideo)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</ListItem>
|
|
);
|
|
};
|
|
|
|
function renderMessageSummary(
|
|
lang: LangFn, message: ApiMessage, blobUrl?: string, searchQuery?: string, isRoundVideo?: boolean,
|
|
) {
|
|
if (!blobUrl) {
|
|
return renderText(getMessageSummaryText(lang, message));
|
|
}
|
|
|
|
return (
|
|
<span className="media-preview">
|
|
<img src={blobUrl} alt="" className={isRoundVideo ? 'round' : undefined} />
|
|
{getMessageVideo(message) && <i className="icon-play" />}
|
|
{renderText(getMessageSummaryText(lang, message, true), ['emoji', 'highlight'], { highlight: searchQuery })}
|
|
</span>
|
|
);
|
|
}
|
|
|
|
export default memo(withGlobal<OwnProps>(
|
|
(global, { chatId }): StateProps => {
|
|
const chat = selectChat(global, chatId);
|
|
if (!chat) {
|
|
return {};
|
|
}
|
|
|
|
const privateChatUserId = getPrivateChatUserId(chat);
|
|
|
|
return {
|
|
chat,
|
|
...(privateChatUserId && { privateChatUser: selectUser(global, privateChatUserId) }),
|
|
lastSyncTime: global.lastSyncTime,
|
|
};
|
|
},
|
|
(setGlobal, actions): DispatchProps => pick(actions, [
|
|
'focusMessage',
|
|
]),
|
|
)(ChatMessage));
|