mirror of
https://github.com/danog/telegram-tt.git
synced 2024-12-12 00:59:52 +01:00
Message: Support web page videos; Always play videos inline (#1233)
This commit is contained in:
parent
f15d616e20
commit
5facc1e705
@ -27,7 +27,7 @@ import { DELETED_COMMENTS_CHANNEL_ID, LOCAL_MESSAGE_ID_BASE, SERVICE_NOTIFICATIO
|
|||||||
import { pick } from '../../../util/iteratees';
|
import { pick } from '../../../util/iteratees';
|
||||||
import { getApiChatIdFromMtpPeer } from './chats';
|
import { getApiChatIdFromMtpPeer } from './chats';
|
||||||
import { buildStickerFromDocument } from './symbols';
|
import { buildStickerFromDocument } from './symbols';
|
||||||
import { buildApiPhoto, buildApiThumbnailFromStripped, buildApiPhotoSize } from './common';
|
import { buildApiPhoto, buildApiThumbnailFromStripped } from './common';
|
||||||
import { interpolateArray } from '../../../util/waveform';
|
import { interpolateArray } from '../../../util/waveform';
|
||||||
import { getCurrencySign } from '../../../components/middle/helpers/getCurrencySign';
|
import { getCurrencySign } from '../../../components/middle/helpers/getCurrencySign';
|
||||||
import { buildPeer } from '../gramjsBuilders';
|
import { buildPeer } from '../gramjsBuilders';
|
||||||
@ -511,26 +511,25 @@ export function buildWebPage(media: GramJs.TypeMessageMedia): ApiWebPage | undef
|
|||||||
|
|
||||||
const { id, photo, document } = media.webpage;
|
const { id, photo, document } = media.webpage;
|
||||||
|
|
||||||
|
let video;
|
||||||
|
if (document instanceof GramJs.Document && document.mimeType.startsWith('video')) {
|
||||||
|
video = buildVideoFromDocument(document);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: Number(id),
|
id: Number(id),
|
||||||
...pick(media.webpage, [
|
...pick(media.webpage, [
|
||||||
'url',
|
'url',
|
||||||
'displayUrl',
|
'displayUrl',
|
||||||
|
'type',
|
||||||
'siteName',
|
'siteName',
|
||||||
'title',
|
'title',
|
||||||
'description',
|
'description',
|
||||||
|
'duration',
|
||||||
]),
|
]),
|
||||||
photo: photo && photo instanceof GramJs.Photo
|
photo: photo instanceof GramJs.Photo ? buildApiPhoto(photo) : undefined,
|
||||||
? {
|
document: !video && document ? buildApiDocument(document) : undefined,
|
||||||
id: String(photo.id),
|
video,
|
||||||
thumbnail: buildApiThumbnailFromStripped(photo.sizes),
|
|
||||||
sizes: photo.sizes
|
|
||||||
.filter((s: any): s is GramJs.PhotoSize => s instanceof GramJs.PhotoSize)
|
|
||||||
.map(buildApiPhotoSize),
|
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
// TODO support video and embed
|
|
||||||
...(document && { hasDocument: true }),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,7 +292,12 @@ export function isMessageWithMedia(message: GramJs.Message | GramJs.UpdateServic
|
|||||||
|| (
|
|| (
|
||||||
media instanceof GramJs.MessageMediaWebPage
|
media instanceof GramJs.MessageMediaWebPage
|
||||||
&& media.webpage instanceof GramJs.WebPage
|
&& media.webpage instanceof GramJs.WebPage
|
||||||
&& media.webpage.photo instanceof GramJs.Photo
|
&& (
|
||||||
|
media.webpage.photo instanceof GramJs.Photo || (
|
||||||
|
media.webpage.document instanceof GramJs.Document
|
||||||
|
&& media.webpage.document.mimeType.startsWith('video')
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,15 @@ export function addMessageToLocalDb(message: GramJs.Message | GramJs.MessageServ
|
|||||||
localDb.documents[String(message.media.document.id)] = message.media.document;
|
localDb.documents[String(message.media.document.id)] = message.media.document;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
message instanceof GramJs.Message
|
||||||
|
&& message.media instanceof GramJs.MessageMediaWebPage
|
||||||
|
&& message.media.webpage instanceof GramJs.WebPage
|
||||||
|
&& message.media.webpage.document instanceof GramJs.Document
|
||||||
|
) {
|
||||||
|
localDb.documents[String(message.media.webpage.document.id)] = message.media.webpage.document;
|
||||||
|
}
|
||||||
|
|
||||||
if (message instanceof GramJs.MessageService && 'photo' in message.action) {
|
if (message instanceof GramJs.MessageService && 'photo' in message.action) {
|
||||||
addPhotoToLocalDb(message.action.photo);
|
addPhotoToLocalDb(message.action.photo);
|
||||||
}
|
}
|
||||||
|
@ -145,6 +145,11 @@ async function download(
|
|||||||
if (entity.media instanceof GramJs.MessageMediaDocument && entity.media.document instanceof GramJs.Document) {
|
if (entity.media instanceof GramJs.MessageMediaDocument && entity.media.document instanceof GramJs.Document) {
|
||||||
fullSize = entity.media.document.size;
|
fullSize = entity.media.document.size;
|
||||||
}
|
}
|
||||||
|
if (entity.media instanceof GramJs.MessageMediaWebPage
|
||||||
|
&& entity.media.webpage instanceof GramJs.WebPage
|
||||||
|
&& entity.media.webpage.document instanceof GramJs.Document) {
|
||||||
|
fullSize = entity.media.webpage.document.size;
|
||||||
|
}
|
||||||
} else if (entity instanceof GramJs.Photo) {
|
} else if (entity instanceof GramJs.Photo) {
|
||||||
mimeType = 'image/jpeg';
|
mimeType = 'image/jpeg';
|
||||||
} else if (entityType === 'sticker' && sizeType) {
|
} else if (entityType === 'sticker' && sizeType) {
|
||||||
@ -187,6 +192,12 @@ function getMessageMediaMimeType(message: GramJs.Message, sizeType?: string) {
|
|||||||
return message.media.document!.mimeType;
|
return message.media.document!.mimeType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (message.media instanceof GramJs.MessageMediaWebPage
|
||||||
|
&& message.media.webpage instanceof GramJs.WebPage
|
||||||
|
&& message.media.webpage.document instanceof GramJs.Document) {
|
||||||
|
return message.media.webpage.document.mimeType;
|
||||||
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,11 +152,14 @@ export interface ApiWebPage {
|
|||||||
id: number;
|
id: number;
|
||||||
url: string;
|
url: string;
|
||||||
displayUrl: string;
|
displayUrl: string;
|
||||||
|
type?: string;
|
||||||
siteName?: string;
|
siteName?: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
photo?: ApiPhoto;
|
photo?: ApiPhoto;
|
||||||
hasDocument?: true;
|
duration?: number;
|
||||||
|
document?: ApiDocument;
|
||||||
|
video?: ApiVideo;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiMessageForwardInfo {
|
export interface ApiMessageForwardInfo {
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
margin-top: 1.5rem;
|
margin-top: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.without-photo::before {
|
&.without-media::before {
|
||||||
content: attr(data-initial);
|
content: attr(data-initial);
|
||||||
width: 3rem;
|
width: 3rem;
|
||||||
height: 3rem;
|
height: 3rem;
|
||||||
@ -86,7 +86,7 @@
|
|||||||
padding: .25rem 3.75rem 0 0;
|
padding: .25rem 3.75rem 0 0;
|
||||||
|
|
||||||
.Media,
|
.Media,
|
||||||
&.without-photo::before {
|
&.without-media::before {
|
||||||
left: auto;
|
left: auto;
|
||||||
right: 0;
|
right: 0;
|
||||||
}
|
}
|
||||||
|
@ -57,13 +57,14 @@ const WebLink: FC<OwnProps> = ({ message, senderTitle, onMessageClick }) => {
|
|||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
photo,
|
photo,
|
||||||
|
video,
|
||||||
} = linkData;
|
} = linkData;
|
||||||
|
|
||||||
const truncatedDescription = !senderTitle && trimText(description, MAX_TEXT_LENGTH);
|
const truncatedDescription = !senderTitle && trimText(description, MAX_TEXT_LENGTH);
|
||||||
|
|
||||||
const className = buildClassName(
|
const className = buildClassName(
|
||||||
'WebLink scroll-item',
|
'WebLink scroll-item',
|
||||||
!photo && 'without-photo',
|
(!photo && !video) && 'without-media',
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -37,6 +37,7 @@ import {
|
|||||||
getMessagePhoto,
|
getMessagePhoto,
|
||||||
getMessageVideo,
|
getMessageVideo,
|
||||||
getMessageWebPagePhoto,
|
getMessageWebPagePhoto,
|
||||||
|
getMessageWebPageVideo,
|
||||||
getPhotoFullDimensions,
|
getPhotoFullDimensions,
|
||||||
getVideoDimensions,
|
getVideoDimensions,
|
||||||
IDimensions,
|
IDimensions,
|
||||||
@ -107,12 +108,15 @@ const MediaViewer: FC<StateProps & DispatchProps> = ({
|
|||||||
const animationKey = useRef<number>(null);
|
const animationKey = useRef<number>(null);
|
||||||
const isOpen = Boolean(avatarOwner || messageId);
|
const isOpen = Boolean(avatarOwner || messageId);
|
||||||
const webPagePhoto = message ? getMessageWebPagePhoto(message) : undefined;
|
const webPagePhoto = message ? getMessageWebPagePhoto(message) : undefined;
|
||||||
|
const webPageVideo = message ? getMessageWebPageVideo(message) : undefined;
|
||||||
const photo = message ? getMessagePhoto(message) : undefined;
|
const photo = message ? getMessagePhoto(message) : undefined;
|
||||||
const video = message ? getMessageVideo(message) : undefined;
|
const video = message ? getMessageVideo(message) : undefined;
|
||||||
const isWebPagePhoto = Boolean(webPagePhoto);
|
const isWebPagePhoto = Boolean(webPagePhoto);
|
||||||
const isPhoto = Boolean(photo || webPagePhoto);
|
const isWebPageVideo = Boolean(webPageVideo);
|
||||||
const isVideo = Boolean(video);
|
const messageVideo = video || webPageVideo;
|
||||||
const isGif = video ? video.isGif : undefined;
|
const isVideo = Boolean(messageVideo);
|
||||||
|
const isPhoto = Boolean(!isVideo && (photo || webPagePhoto));
|
||||||
|
const isGif = (messageVideo) ? messageVideo.isGif : undefined;
|
||||||
const isFromSharedMedia = origin === MediaViewerOrigin.SharedMedia;
|
const isFromSharedMedia = origin === MediaViewerOrigin.SharedMedia;
|
||||||
const isFromSearch = origin === MediaViewerOrigin.SearchResult;
|
const isFromSearch = origin === MediaViewerOrigin.SearchResult;
|
||||||
const slideAnimation = animationLevel >= 1 ? 'mv-slide' : 'none';
|
const slideAnimation = animationLevel >= 1 ? 'mv-slide' : 'none';
|
||||||
@ -129,10 +133,10 @@ const MediaViewer: FC<StateProps & DispatchProps> = ({
|
|||||||
const [isFooterHidden, setIsFooterHidden] = useState<boolean>(false);
|
const [isFooterHidden, setIsFooterHidden] = useState<boolean>(false);
|
||||||
|
|
||||||
const messageIds = useMemo(() => {
|
const messageIds = useMemo(() => {
|
||||||
return isWebPagePhoto && messageId
|
return (isWebPagePhoto || isWebPageVideo) && messageId
|
||||||
? [messageId]
|
? [messageId]
|
||||||
: getChatMediaMessageIds(chatMessages || {}, collectionIds || [], isFromSharedMedia);
|
: getChatMediaMessageIds(chatMessages || {}, collectionIds || [], isFromSharedMedia);
|
||||||
}, [isWebPagePhoto, messageId, chatMessages, collectionIds, isFromSharedMedia]);
|
}, [isWebPagePhoto, isWebPageVideo, messageId, chatMessages, collectionIds, isFromSharedMedia]);
|
||||||
|
|
||||||
const selectedMediaMessageIndex = messageId ? messageIds.indexOf(messageId) : -1;
|
const selectedMediaMessageIndex = messageId ? messageIds.indexOf(messageId) : -1;
|
||||||
const isFirst = selectedMediaMessageIndex === 0 || selectedMediaMessageIndex === -1;
|
const isFirst = selectedMediaMessageIndex === 0 || selectedMediaMessageIndex === -1;
|
||||||
@ -185,9 +189,11 @@ const MediaViewer: FC<StateProps & DispatchProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const photoDimensions = isPhoto ? getPhotoFullDimensions((
|
const photoDimensions = isPhoto ? getPhotoFullDimensions((
|
||||||
isWebPagePhoto ? getMessageWebPagePhoto(message!) : getMessagePhoto(message!)
|
isWebPagePhoto ? webPagePhoto : photo
|
||||||
|
)!) : undefined;
|
||||||
|
const videoDimensions = isVideo ? getVideoDimensions((
|
||||||
|
isWebPageVideo ? webPageVideo : video
|
||||||
)!) : undefined;
|
)!) : undefined;
|
||||||
const videoDimensions = isVideo ? getVideoDimensions(getMessageVideo(message!)!) : undefined;
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!IS_SINGLE_COLUMN_LAYOUT) {
|
if (!IS_SINGLE_COLUMN_LAYOUT) {
|
||||||
@ -436,7 +442,7 @@ const MediaViewer: FC<StateProps & DispatchProps> = ({
|
|||||||
posterData={bestImageData}
|
posterData={bestImageData}
|
||||||
posterSize={message && calculateMediaViewerDimensions(videoDimensions!, hasFooter, true)}
|
posterSize={message && calculateMediaViewerDimensions(videoDimensions!, hasFooter, true)}
|
||||||
downloadProgress={downloadProgress}
|
downloadProgress={downloadProgress}
|
||||||
fileSize={video!.size}
|
fileSize={messageVideo!.size}
|
||||||
isMediaViewerOpen={isOpen}
|
isMediaViewerOpen={isOpen}
|
||||||
noPlay={!isActive}
|
noPlay={!isActive}
|
||||||
onClose={close}
|
onClose={close}
|
||||||
|
@ -2,7 +2,13 @@ import { ApiMessage } from '../../../api/types';
|
|||||||
import { MediaViewerOrigin } from '../../../types';
|
import { MediaViewerOrigin } from '../../../types';
|
||||||
|
|
||||||
import { ANIMATION_END_DELAY } from '../../../config';
|
import { ANIMATION_END_DELAY } from '../../../config';
|
||||||
import { getMessageContent, getPhotoFullDimensions, getVideoDimensions } from '../../../modules/helpers';
|
import {
|
||||||
|
getMessageContent,
|
||||||
|
getMessageWebPagePhoto,
|
||||||
|
getMessageWebPageVideo,
|
||||||
|
getPhotoFullDimensions,
|
||||||
|
getVideoDimensions,
|
||||||
|
} from '../../../modules/helpers';
|
||||||
import {
|
import {
|
||||||
AVATAR_FULL_DIMENSIONS,
|
AVATAR_FULL_DIMENSIONS,
|
||||||
calculateDimensions,
|
calculateDimensions,
|
||||||
@ -28,9 +34,13 @@ export function animateOpening(
|
|||||||
let isVideo = false;
|
let isVideo = false;
|
||||||
let mediaSize;
|
let mediaSize;
|
||||||
if (message) {
|
if (message) {
|
||||||
const { photo, video, webPage } = getMessageContent(message);
|
const { photo, video } = getMessageContent(message);
|
||||||
isVideo = Boolean(video);
|
const webPagePhoto = getMessageWebPagePhoto(message);
|
||||||
mediaSize = video ? getVideoDimensions(video)! : getPhotoFullDimensions((photo || webPage!.photo)!)!;
|
const webPageVideo = getMessageWebPageVideo(message);
|
||||||
|
isVideo = Boolean(video || webPageVideo);
|
||||||
|
mediaSize = isVideo
|
||||||
|
? getVideoDimensions((video || webPageVideo)!)!
|
||||||
|
: getPhotoFullDimensions((photo || webPagePhoto)!)!;
|
||||||
} else {
|
} else {
|
||||||
mediaSize = AVATAR_FULL_DIMENSIONS;
|
mediaSize = AVATAR_FULL_DIMENSIONS;
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,10 @@
|
|||||||
bottom: .05rem;
|
bottom: .05rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.with-video .media-inner { // TODO add support for video in previews in composer
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.site-title,
|
.site-title,
|
||||||
.site-description {
|
.site-description {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
@ -667,6 +667,8 @@ const Message: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
observeIntersection={observeIntersectionForMedia}
|
observeIntersection={observeIntersectionForMedia}
|
||||||
noAvatars={noAvatars}
|
noAvatars={noAvatars}
|
||||||
shouldAutoLoad={shouldAutoLoadMedia}
|
shouldAutoLoad={shouldAutoLoadMedia}
|
||||||
|
shouldAutoPlay={shouldAutoPlayMedia}
|
||||||
|
lastSyncTime={lastSyncTime}
|
||||||
onMediaClick={handleMediaClick}
|
onMediaClick={handleMediaClick}
|
||||||
onCancelMediaTransfer={handleCancelUpload}
|
onCancelMediaTransfer={handleCancelUpload}
|
||||||
/>
|
/>
|
||||||
|
@ -9,10 +9,11 @@ import { formatMediaDuration } from '../../../util/dateFormat';
|
|||||||
import buildClassName from '../../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
import { calculateVideoDimensions } from '../../common/helpers/mediaDimensions';
|
import { calculateVideoDimensions } from '../../common/helpers/mediaDimensions';
|
||||||
import {
|
import {
|
||||||
canMessagePlayVideoInline,
|
|
||||||
getMediaTransferState,
|
getMediaTransferState,
|
||||||
getMessageMediaFormat,
|
getMessageMediaFormat,
|
||||||
getMessageMediaHash,
|
getMessageMediaHash,
|
||||||
|
getMessageVideo,
|
||||||
|
getMessageWebPageVideo,
|
||||||
isForwardedMessage,
|
isForwardedMessage,
|
||||||
isOwnMessage,
|
isOwnMessage,
|
||||||
} from '../../../modules/helpers';
|
} from '../../../modules/helpers';
|
||||||
@ -62,9 +63,8 @@ const Video: FC<OwnProps> = ({
|
|||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
const videoRef = useRef<HTMLVideoElement>(null);
|
const videoRef = useRef<HTMLVideoElement>(null);
|
||||||
|
|
||||||
const video = message.content.video!;
|
const video = (getMessageVideo(message) || getMessageWebPageVideo(message))!;
|
||||||
const localBlobUrl = video.blobUrl;
|
const localBlobUrl = video.blobUrl;
|
||||||
const canPlayInline = Boolean(localBlobUrl) || canMessagePlayVideoInline(video);
|
|
||||||
|
|
||||||
const isIntersecting = useIsIntersecting(ref, observeIntersection);
|
const isIntersecting = useIsIntersecting(ref, observeIntersection);
|
||||||
|
|
||||||
@ -87,13 +87,13 @@ const Video: FC<OwnProps> = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const fullMediaData = localBlobUrl || mediaData;
|
const fullMediaData = localBlobUrl || mediaData;
|
||||||
const isInline = Boolean(canPlayInline && isIntersecting && fullMediaData);
|
const isInline = Boolean(isIntersecting && fullMediaData);
|
||||||
|
|
||||||
const { isBuffered, bufferingHandlers } = useBuffering(!shouldAutoLoad);
|
const { isBuffered, bufferingHandlers } = useBuffering(!shouldAutoLoad);
|
||||||
const { isUploading, isTransferring, transferProgress } = getMediaTransferState(
|
const { isUploading, isTransferring, transferProgress } = getMediaTransferState(
|
||||||
message,
|
message,
|
||||||
uploadProgress || downloadProgress,
|
uploadProgress || downloadProgress,
|
||||||
shouldDownload && (canPlayInline && !isBuffered),
|
shouldDownload && !isBuffered,
|
||||||
);
|
);
|
||||||
const wasDownloadDisabled = usePrevious(isDownloadAllowed) === false;
|
const wasDownloadDisabled = usePrevious(isDownloadAllowed) === false;
|
||||||
const {
|
const {
|
||||||
@ -107,6 +107,8 @@ const Video: FC<OwnProps> = ({
|
|||||||
setPlayProgress(Math.max(0, e.currentTarget.currentTime - 1));
|
setPlayProgress(Math.max(0, e.currentTarget.currentTime - 1));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const duration = video.duration || (videoRef.current && videoRef.current.duration) || 0;
|
||||||
|
|
||||||
const isOwn = isOwnMessage(message);
|
const isOwn = isOwnMessage(message);
|
||||||
const isForwarded = isForwardedMessage(message);
|
const isForwarded = isForwardedMessage(message);
|
||||||
const { width, height } = dimensions || calculateVideoDimensions(video, isOwn, isForwarded, noAvatars);
|
const { width, height } = dimensions || calculateVideoDimensions(video, isOwn, isForwarded, noAvatars);
|
||||||
@ -122,15 +124,15 @@ const Video: FC<OwnProps> = ({
|
|||||||
if (onCancelUpload) {
|
if (onCancelUpload) {
|
||||||
onCancelUpload(message);
|
onCancelUpload(message);
|
||||||
}
|
}
|
||||||
} else if (canPlayInline && !fullMediaData) {
|
} else if (!fullMediaData) {
|
||||||
setIsDownloadAllowed((isAllowed) => !isAllowed);
|
setIsDownloadAllowed((isAllowed) => !isAllowed);
|
||||||
} else if (canPlayInline && fullMediaData && !isPlayAllowed) {
|
} else if (fullMediaData && !isPlayAllowed) {
|
||||||
setIsPlayAllowed(true);
|
setIsPlayAllowed(true);
|
||||||
videoRef.current!.play();
|
videoRef.current!.play();
|
||||||
} else if (onClick) {
|
} else if (onClick) {
|
||||||
onClick(message.id);
|
onClick(message.id);
|
||||||
}
|
}
|
||||||
}, [isUploading, canPlayInline, fullMediaData, isPlayAllowed, onClick, onCancelUpload, message]);
|
}, [isUploading, fullMediaData, isPlayAllowed, onClick, onCancelUpload, message]);
|
||||||
|
|
||||||
const className = buildClassName('media-inner dark', !isUploading && 'interactive');
|
const className = buildClassName('media-inner dark', !isUploading && 'interactive');
|
||||||
const videoClassName = buildClassName('full-media', transitionClassNames);
|
const videoClassName = buildClassName('full-media', transitionClassNames);
|
||||||
@ -140,9 +142,8 @@ const Video: FC<OwnProps> = ({
|
|||||||
: '';
|
: '';
|
||||||
|
|
||||||
const shouldRenderInlineVideo = isInline;
|
const shouldRenderInlineVideo = isInline;
|
||||||
const shouldRenderHqPreview = !canPlayInline && mediaData;
|
const shouldRenderPlayButton = (isDownloadAllowed && !isPlayAllowed && !shouldRenderSpinner);
|
||||||
const shouldRenderPlayButton = !canPlayInline || (isDownloadAllowed && !isPlayAllowed && !shouldRenderSpinner);
|
const shouldRenderDownloadButton = !isDownloadAllowed;
|
||||||
const shouldRenderDownloadButton = canPlayInline && !isDownloadAllowed;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -189,15 +190,6 @@ const Video: FC<OwnProps> = ({
|
|||||||
<source src={fullMediaData} />
|
<source src={fullMediaData} />
|
||||||
</video>
|
</video>
|
||||||
)}
|
)}
|
||||||
{shouldRenderHqPreview && (
|
|
||||||
<img
|
|
||||||
src={mediaData}
|
|
||||||
className={`full-media ${transitionClassNames}`}
|
|
||||||
width={width}
|
|
||||||
height={height}
|
|
||||||
alt=""
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{shouldRenderPlayButton && (
|
{shouldRenderPlayButton && (
|
||||||
<i className="icon-large-play" />
|
<i className="icon-large-play" />
|
||||||
)}
|
)}
|
||||||
@ -209,13 +201,11 @@ const Video: FC<OwnProps> = ({
|
|||||||
{shouldRenderDownloadButton && (
|
{shouldRenderDownloadButton && (
|
||||||
<i className="icon-download" />
|
<i className="icon-download" />
|
||||||
)}
|
)}
|
||||||
{isTransferring && !canPlayInline ? (
|
{isTransferring ? (
|
||||||
<span className="message-upload-progress">{Math.round(transferProgress * 100)}%</span>
|
|
||||||
) : isTransferring && canPlayInline ? (
|
|
||||||
<span className="message-upload-progress">...</span>
|
<span className="message-upload-progress">...</span>
|
||||||
) : (
|
) : (
|
||||||
<div className="message-media-duration">
|
<div className="message-media-duration">
|
||||||
{video.isGif ? 'GIF' : formatMediaDuration(video.duration - playProgress)}
|
{video.isGif ? 'GIF' : formatMediaDuration(Math.max(duration - playProgress, 0))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -46,6 +46,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.with-video .media-inner{
|
||||||
|
margin-top: 0.5rem !important;
|
||||||
|
margin-bottom: 1rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
&.with-square-photo {
|
&.with-square-photo {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
|
@ -11,6 +11,7 @@ import buildClassName from '../../../util/buildClassName';
|
|||||||
|
|
||||||
import SafeLink from '../../common/SafeLink';
|
import SafeLink from '../../common/SafeLink';
|
||||||
import Photo from './Photo';
|
import Photo from './Photo';
|
||||||
|
import Video from './Video';
|
||||||
|
|
||||||
import './WebPage.scss';
|
import './WebPage.scss';
|
||||||
|
|
||||||
@ -21,7 +22,9 @@ type OwnProps = {
|
|||||||
observeIntersection?: ObserveFn;
|
observeIntersection?: ObserveFn;
|
||||||
noAvatars?: boolean;
|
noAvatars?: boolean;
|
||||||
shouldAutoLoad?: boolean;
|
shouldAutoLoad?: boolean;
|
||||||
|
shouldAutoPlay?: boolean;
|
||||||
inPreview?: boolean;
|
inPreview?: boolean;
|
||||||
|
lastSyncTime?: number;
|
||||||
onMediaClick?: () => void;
|
onMediaClick?: () => void;
|
||||||
onCancelMediaTransfer?: () => void;
|
onCancelMediaTransfer?: () => void;
|
||||||
};
|
};
|
||||||
@ -31,7 +34,9 @@ const WebPage: FC<OwnProps> = ({
|
|||||||
observeIntersection,
|
observeIntersection,
|
||||||
noAvatars,
|
noAvatars,
|
||||||
shouldAutoLoad,
|
shouldAutoLoad,
|
||||||
|
shouldAutoPlay,
|
||||||
inPreview,
|
inPreview,
|
||||||
|
lastSyncTime,
|
||||||
onMediaClick,
|
onMediaClick,
|
||||||
onCancelMediaTransfer,
|
onCancelMediaTransfer,
|
||||||
}) => {
|
}) => {
|
||||||
@ -58,16 +63,16 @@ const WebPage: FC<OwnProps> = ({
|
|||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
photo,
|
photo,
|
||||||
|
video,
|
||||||
} = webPage;
|
} = webPage;
|
||||||
|
const isMediaInteractive = (photo || video) && onMediaClick && !isSquarePhoto;
|
||||||
const isMediaInteractive = photo && onMediaClick && !isSquarePhoto && !webPage.hasDocument;
|
|
||||||
const truncatedDescription = trimText(description, MAX_TEXT_LENGTH);
|
const truncatedDescription = trimText(description, MAX_TEXT_LENGTH);
|
||||||
|
|
||||||
const className = buildClassName(
|
const className = buildClassName(
|
||||||
'WebPage',
|
'WebPage',
|
||||||
photo
|
isSquarePhoto && 'with-square-photo',
|
||||||
? (isSquarePhoto && 'with-square-photo')
|
!photo && !video && !inPreview && 'without-media',
|
||||||
: (!inPreview && 'without-photo'),
|
video && 'with-video',
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -76,7 +81,7 @@ const WebPage: FC<OwnProps> = ({
|
|||||||
data-initial={(siteName || displayUrl)[0]}
|
data-initial={(siteName || displayUrl)[0]}
|
||||||
dir="auto"
|
dir="auto"
|
||||||
>
|
>
|
||||||
{photo && (
|
{photo && !video && (
|
||||||
<Photo
|
<Photo
|
||||||
message={message}
|
message={message}
|
||||||
observeIntersection={observeIntersection}
|
observeIntersection={observeIntersection}
|
||||||
@ -97,6 +102,18 @@ const WebPage: FC<OwnProps> = ({
|
|||||||
<p className="site-description">{renderText(truncatedDescription, ['emoji', 'br'])}</p>
|
<p className="site-description">{renderText(truncatedDescription, ['emoji', 'br'])}</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{!inPreview && video && (
|
||||||
|
<Video
|
||||||
|
message={message}
|
||||||
|
observeIntersection={observeIntersection!}
|
||||||
|
noAvatars={noAvatars}
|
||||||
|
shouldAutoLoad={shouldAutoLoad}
|
||||||
|
shouldAutoPlay={shouldAutoPlay}
|
||||||
|
lastSyncTime={lastSyncTime}
|
||||||
|
onClick={isMediaInteractive ? handleMediaClick : undefined}
|
||||||
|
onCancelUpload={onCancelMediaTransfer}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -313,7 +313,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-content.media {
|
.message-content.media, .WebPage {
|
||||||
.media-inner {
|
.media-inner {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
@ -65,7 +65,7 @@ export function buildContentClassName(
|
|||||||
} else if (webPage) {
|
} else if (webPage) {
|
||||||
classNames.push('web-page');
|
classNames.push('web-page');
|
||||||
|
|
||||||
if (webPage.photo) {
|
if (webPage.photo || webPage.video) {
|
||||||
classNames.push('media');
|
classNames.push('media');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,6 @@ export type IDimensions = {
|
|||||||
|
|
||||||
type Target = 'micro' | 'pictogram' | 'inline' | 'viewerPreview' | 'viewerFull' | 'download';
|
type Target = 'micro' | 'pictogram' | 'inline' | 'viewerPreview' | 'viewerFull' | 'download';
|
||||||
|
|
||||||
const MAX_INLINE_VIDEO_SIZE = 10 * 1024 ** 2; // 10 MB
|
|
||||||
|
|
||||||
export function getMessageContent(message: ApiMessage) {
|
export function getMessageContent(message: ApiMessage) {
|
||||||
return message.content;
|
return message.content;
|
||||||
@ -88,12 +87,24 @@ export function getMessageWebPagePhoto(message: ApiMessage) {
|
|||||||
return webPage ? webPage.photo : undefined;
|
return webPage ? webPage.photo : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getMessageWebPageDocument(message: ApiMessage) {
|
||||||
|
const webPage = getMessageWebPage(message);
|
||||||
|
return webPage ? webPage.document : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMessageWebPageVideo(message: ApiMessage): ApiVideo | undefined {
|
||||||
|
const webPage = getMessageWebPage(message);
|
||||||
|
if (!webPage) return undefined;
|
||||||
|
return webPage.video;
|
||||||
|
}
|
||||||
|
|
||||||
export function getMessageMediaThumbnail(message: ApiMessage) {
|
export function getMessageMediaThumbnail(message: ApiMessage) {
|
||||||
const media = getMessagePhoto(message)
|
const media = getMessagePhoto(message)
|
||||||
|| getMessageVideo(message)
|
|| getMessageVideo(message)
|
||||||
|| getMessageDocument(message)
|
|| getMessageDocument(message)
|
||||||
|| getMessageSticker(message)
|
|| getMessageSticker(message)
|
||||||
|| getMessageWebPagePhoto(message);
|
|| getMessageWebPagePhoto(message)
|
||||||
|
|| getMessageWebPageVideo(message);
|
||||||
|
|
||||||
if (!media) {
|
if (!media) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@ -116,55 +127,51 @@ export function getMessageMediaHash(
|
|||||||
photo, video, sticker, audio, voice, document,
|
photo, video, sticker, audio, voice, document,
|
||||||
} = message.content;
|
} = message.content;
|
||||||
const webPagePhoto = getMessageWebPagePhoto(message);
|
const webPagePhoto = getMessageWebPagePhoto(message);
|
||||||
|
const webPageVideo = getMessageWebPageVideo(message);
|
||||||
|
|
||||||
if (!(photo || video || sticker || webPagePhoto || audio || voice || document)) {
|
const messageVideo = video || webPageVideo;
|
||||||
|
const messagePhoto = photo || webPagePhoto;
|
||||||
|
|
||||||
|
if (!(messagePhoto || messageVideo || sticker || audio || voice || document)) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const base = getMessageKey(message);
|
const base = getMessageKey(message);
|
||||||
|
|
||||||
if (photo || webPagePhoto) {
|
if (messageVideo) {
|
||||||
switch (target) {
|
switch (target) {
|
||||||
case 'micro':
|
case 'micro':
|
||||||
case 'pictogram':
|
case 'pictogram':
|
||||||
return `${base}?size=m`;
|
return `${base}?size=m`;
|
||||||
case 'inline':
|
case 'inline':
|
||||||
if (hasMessageLocalBlobUrl(message)) {
|
return !hasMessageLocalBlobUrl(message) ? getVideoOrAudioBaseHash(messageVideo, base) : undefined;
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${base}?size=x`;
|
|
||||||
case 'viewerPreview':
|
|
||||||
return `${base}?size=x`;
|
|
||||||
case 'viewerFull':
|
|
||||||
return `${base}?size=z`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (video) {
|
|
||||||
switch (target) {
|
|
||||||
case 'micro':
|
|
||||||
case 'pictogram':
|
|
||||||
return `${base}?size=m`;
|
|
||||||
case 'inline':
|
|
||||||
if (hasMessageLocalBlobUrl(message)) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (canMessagePlayVideoInline(video)) {
|
|
||||||
return getVideoOrAudioBaseHash(video, base);
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${base}?size=z`;
|
|
||||||
case 'viewerPreview':
|
case 'viewerPreview':
|
||||||
return `${base}?size=m`;
|
return `${base}?size=m`;
|
||||||
case 'viewerFull':
|
case 'viewerFull':
|
||||||
return getVideoOrAudioBaseHash(video, base);
|
return getVideoOrAudioBaseHash(messageVideo, base);
|
||||||
case 'download':
|
case 'download':
|
||||||
return `${base}?download`;
|
return `${base}?download`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (messagePhoto) {
|
||||||
|
switch (target) {
|
||||||
|
case 'micro':
|
||||||
|
case 'pictogram':
|
||||||
|
return `${base}?size=m`;
|
||||||
|
case 'inline':
|
||||||
|
if (hasMessageLocalBlobUrl(message)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${base}?size=x`;
|
||||||
|
case 'viewerPreview':
|
||||||
|
return `${base}?size=x`;
|
||||||
|
case 'viewerFull':
|
||||||
|
return `${base}?size=z`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (document) {
|
if (document) {
|
||||||
switch (target) {
|
switch (target) {
|
||||||
case 'micro':
|
case 'micro':
|
||||||
@ -235,10 +242,12 @@ export function getMessageMediaFormat(
|
|||||||
sticker, video, audio, voice,
|
sticker, video, audio, voice,
|
||||||
} = message.content;
|
} = message.content;
|
||||||
|
|
||||||
|
const fullVideo = video || getMessageWebPageVideo(message);
|
||||||
|
|
||||||
if (sticker && target === 'inline' && sticker.isAnimated) {
|
if (sticker && target === 'inline' && sticker.isAnimated) {
|
||||||
return ApiMediaFormat.Lottie;
|
return ApiMediaFormat.Lottie;
|
||||||
} else if (video && IS_PROGRESSIVE_SUPPORTED && (
|
} else if (fullVideo && IS_PROGRESSIVE_SUPPORTED && (
|
||||||
(target === 'viewerFull') || (target === 'inline' && canMessagePlayVideoInline(video))
|
target === 'viewerFull' || target === 'inline'
|
||||||
)) {
|
)) {
|
||||||
return ApiMediaFormat.Progressive;
|
return ApiMediaFormat.Progressive;
|
||||||
} else if (audio || voice) {
|
} else if (audio || voice) {
|
||||||
@ -254,12 +263,18 @@ export function getMessageMediaFormat(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getMessageMediaFilename(message: ApiMessage) {
|
export function getMessageMediaFilename(message: ApiMessage) {
|
||||||
const { photo, video, webPage } = message.content;
|
const { photo, video } = message.content;
|
||||||
|
const webPagePhoto = getMessageWebPagePhoto(message);
|
||||||
|
const webPageVideo = getMessageWebPageVideo(message);
|
||||||
|
|
||||||
if (photo || (webPage && webPage.photo)) {
|
if (photo || webPagePhoto) {
|
||||||
return `photo${message.date}.jpeg`;
|
return `photo${message.date}.jpeg`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (webPageVideo) {
|
||||||
|
return webPageVideo.fileName;
|
||||||
|
}
|
||||||
|
|
||||||
if (video) {
|
if (video) {
|
||||||
return video.fileName;
|
return video.fileName;
|
||||||
}
|
}
|
||||||
@ -273,10 +288,6 @@ export function hasMessageLocalBlobUrl(message: ApiMessage) {
|
|||||||
return (photo && photo.blobUrl) || (video && video.blobUrl) || (document && document.previewBlobUrl);
|
return (photo && photo.blobUrl) || (video && video.blobUrl) || (document && document.previewBlobUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function canMessagePlayVideoInline(video: ApiVideo): boolean {
|
|
||||||
return video.isGif || video.isRound || video.size <= MAX_INLINE_VIDEO_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getChatMediaMessageIds(
|
export function getChatMediaMessageIds(
|
||||||
messages: Record<number, ApiMessage>, listedIds: number[], reverseOrder = false,
|
messages: Record<number, ApiMessage>, listedIds: number[], reverseOrder = false,
|
||||||
) {
|
) {
|
||||||
@ -360,7 +371,7 @@ export function getMessageContentIds(
|
|||||||
|
|
||||||
export function getMediaDuration(message: ApiMessage) {
|
export function getMediaDuration(message: ApiMessage) {
|
||||||
const { audio, voice, video } = getMessageContent(message);
|
const { audio, voice, video } = getMessageContent(message);
|
||||||
const media = audio || voice || video;
|
const media = audio || voice || video || getMessageWebPageVideo(message);
|
||||||
if (!media) {
|
if (!media) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ import {
|
|||||||
isChatGroup,
|
isChatGroup,
|
||||||
isChatSuperGroup,
|
isChatSuperGroup,
|
||||||
getMessageVideo,
|
getMessageVideo,
|
||||||
|
getMessageWebPageVideo,
|
||||||
} from '../helpers';
|
} from '../helpers';
|
||||||
import { findLast } from '../../util/iteratees';
|
import { findLast } from '../../util/iteratees';
|
||||||
import { selectIsStickerFavorite } from './symbols';
|
import { selectIsStickerFavorite } from './symbols';
|
||||||
@ -679,7 +680,7 @@ export function selectShouldAutoLoadMedia(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function selectShouldAutoPlayMedia(global: GlobalState, message: ApiMessage) {
|
export function selectShouldAutoPlayMedia(global: GlobalState, message: ApiMessage) {
|
||||||
const video = getMessageVideo(message);
|
const video = getMessageVideo(message) || getMessageWebPageVideo(message);
|
||||||
if (!video) {
|
if (!video) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user