import type { FC } from '../../lib/teact/teact'; import React, { memo, useCallback } from '../../lib/teact/teact'; import { withGlobal } from '../../global'; import type { ApiChat, ApiDimensions, ApiMessage, ApiUser, } from '../../api/types'; import { MediaViewerOrigin } from '../../types'; import { IS_SINGLE_COLUMN_LAYOUT, IS_TOUCH_ENV } from '../../util/environment'; import { selectChat, selectChatMessage, selectIsMessageProtected, selectScheduledMessage, selectUser, } from '../../global/selectors'; import { calculateMediaViewerDimensions } from '../common/helpers/mediaDimensions'; import { renderMessageText } from '../common/helpers/renderMessageText'; import stopEvent from '../../util/stopEvent'; import buildClassName from '../../util/buildClassName'; import { useMediaProps } from './hooks/useMediaProps'; import Spinner from '../ui/Spinner'; import MediaViewerFooter from './MediaViewerFooter'; import VideoPlayer from './VideoPlayer'; import './MediaViewerContent.scss'; type OwnProps = { mediaId?: number; chatId?: string; threadId?: number; avatarOwnerId?: string; origin?: MediaViewerOrigin; isActive?: boolean; animationLevel: 0 | 1 | 2; onClose: () => void; onFooterClick: () => void; setIsFooterHidden?: (isHidden: boolean) => void; isFooterHidden?: boolean; }; type StateProps = { chatId?: string; mediaId?: number; senderId?: string; threadId?: number; avatarOwner?: ApiChat | ApiUser; message?: ApiMessage; origin?: MediaViewerOrigin; isProtected?: boolean; volume: number; isMuted: boolean; playbackRate: number; }; const ANIMATION_DURATION = 350; const MediaViewerContent: FC = (props) => { const { mediaId, isActive, avatarOwner, chatId, message, origin, animationLevel, isFooterHidden, isProtected, volume, playbackRate, isMuted, onClose, onFooterClick, setIsFooterHidden, } = props; const isGhostAnimation = animationLevel === 2; const { isVideo, isPhoto, bestImageData, dimensions, isGif, isVideoAvatar, localBlobUrl, fullMediaBlobUrl, previewBlobUrl, pictogramBlobUrl, videoSize, loadProgress, } = useMediaProps({ message, avatarOwner, mediaId, origin, delay: isGhostAnimation && ANIMATION_DURATION, }); const isOpen = Boolean(avatarOwner || mediaId); const toggleControls = useCallback((isVisible) => { setIsFooterHidden?.(!isVisible); }, [setIsFooterHidden]); const handleMouseMove = useCallback(() => { toggleControls(true); }, [toggleControls]); const handleMouseOut = useCallback(() => { toggleControls(false); }, [toggleControls]); if (avatarOwner) { if (!isVideoAvatar) { return (
{renderPhoto( fullMediaBlobUrl || previewBlobUrl, calculateMediaViewerDimensions(dimensions, false), !IS_SINGLE_COLUMN_LAYOUT && !isProtected, isProtected, )}
); } else { return (
); } } if (!message) return undefined; const textParts = renderMessageText(message); const hasFooter = Boolean(textParts); return (
{isPhoto && renderPhoto( localBlobUrl || fullMediaBlobUrl || previewBlobUrl || pictogramBlobUrl, message && calculateMediaViewerDimensions(dimensions!, hasFooter), !IS_SINGLE_COLUMN_LAYOUT && !isProtected, isProtected, )} {isVideo && (!isActive ? renderVideoPreview( bestImageData, message && calculateMediaViewerDimensions(dimensions!, hasFooter, true), !IS_SINGLE_COLUMN_LAYOUT && !isProtected, isProtected, ) : ( ))} {textParts && ( )}
); }; export default memo(withGlobal( (global, ownProps): StateProps => { const { chatId, threadId, mediaId, avatarOwnerId, origin, } = ownProps; const { volume, isMuted, playbackRate, } = global.mediaViewer; if (origin === MediaViewerOrigin.SearchResult) { if (!(chatId && mediaId)) { return { volume, isMuted, playbackRate }; } const message = selectChatMessage(global, chatId, mediaId); if (!message) { return { volume, isMuted, playbackRate }; } return { chatId, mediaId, senderId: message.senderId, origin, message, isProtected: selectIsMessageProtected(global, message), volume, isMuted, playbackRate, }; } if (avatarOwnerId) { const sender = selectUser(global, avatarOwnerId) || selectChat(global, avatarOwnerId); return { mediaId, senderId: avatarOwnerId, avatarOwner: sender, origin, volume, isMuted, playbackRate, }; } if (!(chatId && threadId && mediaId)) { return { volume, isMuted, playbackRate }; } let message: ApiMessage | undefined; if (origin && [MediaViewerOrigin.ScheduledAlbum, MediaViewerOrigin.ScheduledInline].includes(origin)) { message = selectScheduledMessage(global, chatId, mediaId); } else { message = selectChatMessage(global, chatId, mediaId); } if (!message) { return { volume, isMuted, playbackRate }; } return { chatId, threadId, mediaId, senderId: message.senderId, origin, message, isProtected: selectIsMessageProtected(global, message), volume, isMuted, playbackRate, }; }, )(MediaViewerContent)); function renderPhoto(blobUrl?: string, imageSize?: ApiDimensions, canDrag?: boolean, isProtected?: boolean) { return blobUrl ? (
{isProtected &&
}
) : (
); } function renderVideoPreview(blobUrl?: string, imageSize?: ApiDimensions, canDrag?: boolean, isProtected?: boolean) { const wrapperStyle = imageSize && `width: ${imageSize.width}px; height: ${imageSize.height}px`; const videoStyle = `background-image: url(${blobUrl})`; return blobUrl ? (
{isProtected &&
}
{/* eslint-disable-next-line jsx-a11y/media-has-caption */}
) : (
); }