// @flow // This component is entirely for triggering the start of a file view // The actual viewer for a file exists in TextViewer and FileRenderFloating // They can't exist in one component because we need to handle/listen for the start of a new file view // while a file is currently being viewed import { useIsMobile } from 'effects/use-screensize'; import React from 'react'; import classnames from 'classnames'; import * as PAGES from 'constants/pages'; import * as RENDER_MODES from 'constants/file_render_modes'; import Button from 'component/button'; import { getThumbnailCdnUrl } from 'util/thumbnail'; import Nag from 'component/common/nag'; // $FlowFixMe cannot resolve ... import FileRenderPlaceholder from 'static/img/fileRenderPlaceholder.png'; import * as COLLECTIONS_CONSTS from 'constants/collections'; import { LayoutRenderContext } from 'page/livestream/view'; import { formatLbryUrlForWeb } from 'util/url'; import { LIVESTREAM_STATUS_CHECK_INTERVAL } from 'constants/livestream'; import FileViewerEmbeddedTitle from 'component/fileViewerEmbeddedTitle'; type Props = { channelClaimId: ?string, isPlaying: boolean, fileInfo: FileListItem, uri: string, history: { push: (string) => void }, location: { search: ?string, pathname: string, href: string, state: { forceAutoplay: boolean } }, obscurePreview: boolean, insufficientCredits: boolean, claimThumbnail?: string, autoplay: boolean, costInfo: any, inline: boolean, renderMode: string, claimWasPurchased: boolean, authenticated: boolean, videoTheaterMode: boolean, isCurrentClaimLive?: boolean, isLivestreamClaim: boolean, customAction?: any, embedded?: boolean, parentCommentId?: string, isMarkdownPost?: boolean, doUriInitiatePlay: (playingOptions: PlayingUri, isPlayable: boolean) => void, doFetchChannelLiveStatus: (string) => void, }; export default function FileRenderInitiator(props: Props) { const { channelClaimId, isPlaying, fileInfo, uri, obscurePreview, insufficientCredits, history, location, claimThumbnail, autoplay, renderMode, costInfo, claimWasPurchased, authenticated, videoTheaterMode, isCurrentClaimLive, isLivestreamClaim, customAction, embedded, parentCommentId, isMarkdownPost, doUriInitiatePlay, doFetchChannelLiveStatus, } = props; const layountRendered = React.useContext(LayoutRenderContext); const isMobile = useIsMobile(); const containerRef = React.useRef(); const [thumbnail, setThumbnail] = React.useState(FileRenderPlaceholder); const { search, href, state: locationState, pathname } = location; const urlParams = search && new URLSearchParams(search); const collectionId = urlParams && urlParams.get(COLLECTIONS_CONSTS.COLLECTION_ID); // check if there is a time or autoplay parameter, if so force autoplay const urlTimeParam = href && href.indexOf('t=') > -1; const forceAutoplayParam = locationState && locationState.forceAutoplay; const shouldAutoplay = !embedded && (forceAutoplayParam || urlTimeParam || autoplay); const isFree = costInfo && costInfo.cost === 0; const canViewFile = isLivestreamClaim ? (layountRendered || isMobile) && isCurrentClaimLive : isFree || claimWasPurchased; const isPlayable = RENDER_MODES.FLOATING_MODES.includes(renderMode) || isCurrentClaimLive; const isText = RENDER_MODES.TEXT_MODES.includes(renderMode); const renderUnsupported = RENDER_MODES.UNSUPPORTED_IN_THIS_APP.includes(renderMode); const disabled = (isLivestreamClaim && !isCurrentClaimLive) || renderUnsupported || (!fileInfo && insufficientCredits && !claimWasPurchased); const shouldRedirect = !authenticated && !isFree; function doAuthRedirect() { history.push(`/$/${PAGES.AUTH}?redirect=${encodeURIComponent(pathname)}`); } // Find out current channels status + active live claim React.useEffect(() => { if (!channelClaimId || !isLivestreamClaim) return; const fetch = () => doFetchChannelLiveStatus(channelClaimId); const intervalId = setInterval(fetch, LIVESTREAM_STATUS_CHECK_INTERVAL); return () => clearInterval(intervalId); }, [channelClaimId, doFetchChannelLiveStatus, isLivestreamClaim]); React.useEffect(() => { if (!claimThumbnail) return; setTimeout(() => { let newThumbnail = claimThumbnail; if ( containerRef.current && containerRef.current.parentElement && containerRef.current.parentElement.offsetWidth ) { const w = containerRef.current.parentElement.offsetWidth; newThumbnail = getThumbnailCdnUrl({ thumbnail: newThumbnail, width: w, height: w }); } if (newThumbnail !== thumbnail) { setThumbnail(newThumbnail); } }, 200); }, [claimThumbnail, thumbnail]); function handleClick() { if (embedded && !isPlayable) { const formattedUrl = formatLbryUrlForWeb(uri); history.push(formattedUrl); } else { viewFile(); } } // Wrap this in useCallback because we need to use it to the view effect // If we don't a new instance will be created for every render and react will think the dependencies have changed, which will add/remove the listener for every render const viewFile = React.useCallback(() => { const playingOptions = { uri, collectionId, pathname, source: undefined, commentId: undefined }; if (parentCommentId) { playingOptions.source = 'comment'; playingOptions.commentId = parentCommentId; } else if (isMarkdownPost) { playingOptions.source = 'markdown'; } doUriInitiatePlay(playingOptions, isPlayable); }, [collectionId, doUriInitiatePlay, isMarkdownPost, isPlayable, parentCommentId, pathname, uri]); React.useEffect(() => { const videoOnPage = document.querySelector('video'); if ( (canViewFile || forceAutoplayParam) && ((shouldAutoplay && (!videoOnPage || forceAutoplayParam) && isPlayable) || RENDER_MODES.AUTO_RENDER_MODES.includes(renderMode)) ) { viewFile(); } }, [canViewFile, forceAutoplayParam, isPlayable, renderMode, shouldAutoplay, viewFile]); /* once content is playing, let the appropriate take care of it... but for playables, always render so area can be used to fill with floating player */ if (isPlaying && !isPlayable && canViewFile && !collectionId) { return null; } return (
{embedded && } {renderUnsupported ? ( ) : ( !claimWasPurchased && insufficientCredits && ( history.push(`/$/${PAGES.REWARDS}`)} /> ) )} {(!disabled || (embedded && isLivestreamClaim)) && (
); }