import type { RefObject } from 'react'; import type { FC } from '../../lib/teact/teact'; import React, { memo } from '../../lib/teact/teact'; import type { MessageListType } from '../../global/types'; import { SCHEDULED_WHEN_ONLINE } from '../../config'; import buildClassName from '../../util/buildClassName'; import { compact } from '../../util/iteratees'; import { formatHumanDate } from '../../util/dateFormat'; import { getMessageHtmlId, getMessageOriginalId, isActionMessage, isOwnMessage, isServiceNotificationMessage, } from '../../global/helpers'; import useLang from '../../hooks/useLang'; import type { MessageDateGroup } from './helpers/groupMessages'; import { isAlbum } from './helpers/groupMessages'; import { preventMessageInputBlur } from './helpers/preventMessageInputBlur'; import useScrollHooks from './hooks/useScrollHooks'; import useMessageObservers from './hooks/useMessageObservers'; import Message from './message/Message'; import SponsoredMessage from './message/SponsoredMessage'; import ActionMessage from './ActionMessage'; import { getActions } from '../../global'; import { MAIN_THREAD_ID } from '../../api/types'; interface OwnProps { isCurrentUserPremium?: boolean; chatId: string; threadId: number; messageIds: number[]; messageGroups: MessageDateGroup[]; isViewportNewest: boolean; isUnread: boolean; withUsers: boolean; noAvatars: boolean; containerRef: RefObject; anchorIdRef: { current: string | undefined }; memoUnreadDividerBeforeIdRef: { current: number | undefined }; memoFirstUnreadIdRef: { current: number | undefined }; type: MessageListType; isReady: boolean; isScrollingRef: { current: boolean | undefined }; isScrollPatchNeededRef: { current: boolean | undefined }; threadTopMessageId: number | undefined; hasLinkedChat: boolean | undefined; isSchedule: boolean; noAppearanceAnimation: boolean; onFabToggle: AnyToVoidFunction; onNotchToggle: AnyToVoidFunction; } const UNREAD_DIVIDER_CLASS = 'unread-divider'; const MessageListContent: FC = ({ isCurrentUserPremium, chatId, threadId, messageIds, messageGroups, isViewportNewest, isUnread, withUsers, noAvatars, containerRef, anchorIdRef, memoUnreadDividerBeforeIdRef, memoFirstUnreadIdRef, type, isReady, isScrollingRef, isScrollPatchNeededRef, threadTopMessageId, hasLinkedChat, isSchedule, noAppearanceAnimation, onFabToggle, onNotchToggle, }) => { const { openHistoryCalendar } = getActions(); const { observeIntersectionForReading, observeIntersectionForLoading, observeIntersectionForPlaying, } = useMessageObservers(type, containerRef, memoFirstUnreadIdRef); const { backwardsTriggerRef, forwardsTriggerRef, fabTriggerRef, } = useScrollHooks( type, containerRef, messageIds, isViewportNewest, isUnread, onFabToggle, onNotchToggle, isReady, isScrollingRef, isScrollPatchNeededRef, ); const lang = useLang(); const unreadDivider = (
{lang('UnreadMessages')}
); const messageCountToAnimate = noAppearanceAnimation ? 0 : messageGroups.reduce((acc, messageGroup) => { return acc + messageGroup.senderGroups.flat().length; }, 0); let appearanceIndex = 0; const dateGroups = messageGroups.map(( dateGroup: MessageDateGroup, dateGroupIndex: number, dateGroupsArray: MessageDateGroup[], ) => { const senderGroups = dateGroup.senderGroups.map(( senderGroup, senderGroupIndex, senderGroupsArray, ) => { if ( senderGroup.length === 1 && !isAlbum(senderGroup[0]) && isActionMessage(senderGroup[0]) && !senderGroup[0].content.action?.phoneCall ) { const message = senderGroup[0]!; const isLastInList = ( senderGroupIndex === senderGroupsArray.length - 1 && dateGroupIndex === dateGroupsArray.length - 1 ); return compact([ message.id === memoUnreadDividerBeforeIdRef.current && unreadDivider, , ]); } let currentDocumentGroupId: string | undefined; return senderGroup.map(( messageOrAlbum, messageIndex, ) => { const message = isAlbum(messageOrAlbum) ? messageOrAlbum.mainMessage : messageOrAlbum; const album = isAlbum(messageOrAlbum) ? messageOrAlbum : undefined; const isOwn = isOwnMessage(message); const isMessageAlbum = isAlbum(messageOrAlbum); const nextMessage = senderGroup[messageIndex + 1]; if (message.previousLocalId && anchorIdRef.current === getMessageHtmlId(message.previousLocalId)) { anchorIdRef.current = getMessageHtmlId(message.id); } const documentGroupId = !isMessageAlbum && message.groupedId ? message.groupedId : undefined; const nextDocumentGroupId = nextMessage && !isAlbum(nextMessage) ? nextMessage.groupedId : undefined; const position = { isFirstInGroup: messageIndex === 0, isLastInGroup: messageIndex === senderGroup.length - 1, isFirstInDocumentGroup: Boolean(documentGroupId && documentGroupId !== currentDocumentGroupId), isLastInDocumentGroup: Boolean(documentGroupId && documentGroupId !== nextDocumentGroupId), isLastInList: ( messageIndex === senderGroup.length - 1 && senderGroupIndex === senderGroupsArray.length - 1 && dateGroupIndex === dateGroupsArray.length - 1 ), }; currentDocumentGroupId = documentGroupId; const originalId = getMessageOriginalId(message); // Service notifications saved in cache in previous versions may share the same `previousLocalId` const key = isServiceNotificationMessage(message) ? `${message.date}_${originalId}` : originalId; return compact([ message.id === memoUnreadDividerBeforeIdRef.current && unreadDivider, , message.id === threadTopMessageId && (
{lang('DiscussionStarted')}
), ]); }).flat(); }); return (
openHistoryCalendar({ selectedAt: dateGroup.datetime }) : undefined} > {isSchedule && dateGroup.originalDate === SCHEDULED_WHEN_ONLINE && ( lang('MessageScheduledUntilOnline') )} {isSchedule && dateGroup.originalDate !== SCHEDULED_WHEN_ONLINE && ( lang('MessageScheduledOn', formatHumanDate(lang, dateGroup.datetime, undefined, true)) )} {!isSchedule && formatHumanDate(lang, dateGroup.datetime)}
{senderGroups.flat()}
); }); return (
{dateGroups.flat()} {!isCurrentUserPremium && isViewportNewest && ( )}
); }; export default memo(MessageListContent);