import type { FC } from '../../../lib/teact/teact'; import type React from '../../../lib/teact/teact'; import { memo, useMemo, useRef } from '../../../lib/teact/teact'; import { getActions, withGlobal } from '../../../global'; import type { ApiMessage, ApiMessageWebPage, ApiTypeStory, ApiWebPage, ApiWebPageFull } from '../../../api/types'; import type { ObserveFn } from '../../../hooks/useIntersectionObserver'; import { AudioOrigin, type ThemeKey, type WebPageMediaSize } from '../../../types'; import { getPhotoFullDimensions } from '../../../global/helpers'; import { selectCanPlayAnimatedEmojis } from '../../../global/selectors'; import buildClassName from '../../../util/buildClassName'; import { tryParseDeepLink } from '../../../util/deepLinkParser'; import trimText from '../../../util/trimText'; import renderText from '../../common/helpers/renderText'; import { getWebpageButtonLangKey } from './helpers/webpageType'; import useDynamicColorListener from '../../../hooks/stickers/useDynamicColorListener'; import useEnsureStory from '../../../hooks/useEnsureStory'; import useLang from '../../../hooks/useLang'; import useLastCallback from '../../../hooks/useLastCallback'; import Audio from '../../common/Audio'; import Document from '../../common/Document'; import EmojiIconBackground from '../../common/embedded/EmojiIconBackground'; import PeerColorWrapper from '../../common/PeerColorWrapper'; import SafeLink from '../../common/SafeLink'; import StickerView from '../../common/StickerView'; import Button from '../../ui/Button'; import BaseStory from './BaseStory'; import Photo from './Photo'; import Video from './Video'; import WebPageUniqueGift from './WebPageUniqueGift'; import './WebPage.scss'; const MAX_TEXT_LENGTH = 170; // symbols const WEBPAGE_STORY_TYPE = 'telegram_story'; const WEBPAGE_GIFT_TYPE = 'telegram_nft'; const STICKER_SIZE = 80; const EMOJI_SIZE = 38; type OwnProps = { messageWebPage: ApiMessageWebPage; webPage: ApiWebPage; message?: ApiMessage; noAvatars?: boolean; canAutoLoad?: boolean; canAutoPlay?: boolean; asForwarded?: boolean; isDownloading?: boolean; isProtected?: boolean; isConnected?: boolean; backgroundEmojiId?: string; theme: ThemeKey; story?: ApiTypeStory; shouldWarnAboutFiles?: boolean; autoLoadFileMaxSizeMb?: number; lastPlaybackTimestamp?: number; observeIntersectionForLoading?: ObserveFn; observeIntersectionForPlaying?: ObserveFn; onAudioPlay?: NoneToVoidFunction; onMediaClick?: NoneToVoidFunction; onDocumentClick?: NoneToVoidFunction; onCancelMediaTransfer?: NoneToVoidFunction; onContainerClick?: ((e: React.MouseEvent) => void); }; type StateProps = { canPlayAnimatedEmojis: boolean; }; const WebPage: FC = ({ messageWebPage, webPage, message, noAvatars, canAutoLoad, canAutoPlay, asForwarded, isDownloading = false, isProtected, isConnected, story, theme, backgroundEmojiId, shouldWarnAboutFiles, autoLoadFileMaxSizeMb, lastPlaybackTimestamp, observeIntersectionForLoading, observeIntersectionForPlaying, onMediaClick, onDocumentClick, onContainerClick, onAudioPlay, onCancelMediaTransfer, }) => { const { openUrl, openTelegramLink } = getActions(); const stickersRef = useRef(); const lang = useLang(); const handleMediaClick = useLastCallback(() => { onMediaClick!(); }); const handleContainerClick = useLastCallback((e: React.MouseEvent) => { onContainerClick?.(e); }); const fullWebPage = webPage?.webpageType === 'full' ? webPage : undefined; const { story: storyData, stickers } = fullWebPage || {}; useEnsureStory(storyData?.peerId, storyData?.id, story); const hasCustomColor = stickers?.isWithTextColor || stickers?.documents?.[0]?.shouldUseTextColor; const customColor = useDynamicColorListener(stickersRef, undefined, !hasCustomColor); const linkTimestamp = useMemo(() => { const parsedLink = webPage?.url && tryParseDeepLink(webPage?.url); if (!parsedLink || !('timestamp' in parsedLink)) return undefined; return parsedLink.timestamp; }, [webPage?.url]); if (webPage?.webpageType !== 'full') return undefined; const handleOpenTelegramLink = useLastCallback(() => { openTelegramLink({ url: webPage.url, }); }); const { siteName, url, displayUrl, title, description, photo, video, audio, type, document, } = webPage; const { mediaSize } = messageWebPage; const isStory = type === WEBPAGE_STORY_TYPE; const isGift = type === WEBPAGE_GIFT_TYPE; const isExpiredStory = story && 'isDeleted' in story; const resultType = stickers?.isEmoji ? 'telegram_emojiset' : type; const quickButtonLangKey = !isExpiredStory ? getWebpageButtonLangKey(resultType) : undefined; const quickButtonTitle = quickButtonLangKey && lang(quickButtonLangKey); const truncatedDescription = trimText(description, MAX_TEXT_LENGTH); const isArticle = Boolean(truncatedDescription || title || siteName); let isSquarePhoto = Boolean(stickers); if (isArticle && webPage?.photo && !webPage.video && !webPage.document) { isSquarePhoto = getIsSmallPhoto(webPage, mediaSize); } const isMediaInteractive = (photo || video) && onMediaClick && !isSquarePhoto; const className = buildClassName( 'WebPage', isSquarePhoto && 'with-square-photo', !photo && !video && 'without-media', video && 'with-video', !isArticle && 'no-article', document && 'with-document', quickButtonTitle && 'with-quick-button', isGift && 'with-gift', ); function renderQuickButton(caption: string) { return ( ); } return (
{backgroundEmojiId && ( )} {isStory && ( )} {isGift && ( )} {isArticle && (
openUrl({ url, shouldSkipModal: messageWebPage.isSafe })} > {title && (

{renderText(title)}

)} {truncatedDescription && !isGift && (

{renderText(truncatedDescription, ['emoji', 'br'])}

)}
)} {photo && !isGift && !video && !document && ( )} {video && (
{quickButtonTitle && renderQuickButton(quickButtonTitle)}
); }; function getIsSmallPhoto(webPage: ApiWebPageFull, mediaSize?: WebPageMediaSize) { if (!webPage?.photo) return false; if (mediaSize === 'small') return true; if (mediaSize === 'large') return false; const { width, height } = getPhotoFullDimensions(webPage.photo) || {}; if (!width || !height) return false; return width === height && !webPage.hasLargeMedia; } export default memo(withGlobal( (global): Complete => { return { canPlayAnimatedEmojis: selectCanPlayAnimatedEmojis(global), }; }, )(WebPage));