lbry-desktop/ui/component/fileRenderInitiator/view.jsx

200 lines
6.5 KiB
React
Raw Permalink Normal View History

2019-08-13 07:35:13 +02:00
// @flow
// This component is entirely for triggering the start of a file view
2020-04-14 01:48:11 +02:00
// The actual viewer for a file exists in TextViewer and FileRenderFloating
2019-08-13 07:35:13 +02:00
// 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 React, { useEffect, useCallback } from 'react';
2019-08-13 07:35:13 +02:00
import classnames from 'classnames';
import * as PAGES from 'constants/pages';
import * as RENDER_MODES from 'constants/file_render_modes';
2021-08-31 09:05:42 +02:00
import * as KEYCODES from 'constants/keycodes';
2019-08-13 07:35:13 +02:00
import Button from 'component/button';
import isUserTyping from 'util/detect-typing';
import { getThumbnailCdnUrl } from 'util/thumbnail';
import Nag from 'component/common/nag';
// $FlowFixMe cannot resolve ...
import FileRenderPlaceholder from 'static/img/fileRenderPlaceholder.png';
2019-08-13 07:35:13 +02:00
type Props = {
play: (string, string, boolean) => void,
2019-08-13 07:35:13 +02:00
isPlaying: boolean,
fileInfo: FileListItem,
uri: string,
2021-03-11 18:08:11 +01:00
history: { push: (string) => void },
location: { search: ?string, pathname: string, href: string, state: { forceAutoplay: boolean } },
2019-08-13 07:35:13 +02:00
obscurePreview: boolean,
insufficientCredits: boolean,
claimThumbnail?: string,
2019-08-13 07:35:13 +02:00
autoplay: boolean,
2019-08-29 01:33:38 +02:00
costInfo: any,
2020-01-06 19:32:35 +01:00
inline: boolean,
renderMode: string,
claim: StreamClaim,
2020-05-21 17:38:28 +02:00
claimWasPurchased: boolean,
authenticated: boolean,
2021-01-08 16:21:27 +01:00
videoTheaterMode: boolean,
collectionId: string,
2019-08-13 07:35:13 +02:00
};
export default function FileRenderInitiator(props: Props) {
2019-08-14 05:09:25 +02:00
const {
play,
isPlaying,
fileInfo,
uri,
obscurePreview,
insufficientCredits,
history,
2020-05-21 17:38:28 +02:00
location,
claimThumbnail,
renderMode,
2019-08-29 01:33:38 +02:00
costInfo,
2020-05-21 17:38:28 +02:00
claimWasPurchased,
authenticated,
2021-01-08 16:21:27 +01:00
videoTheaterMode,
collectionId,
2019-08-14 05:09:25 +02:00
} = props;
// check if there is a time or autoplay parameter, if so force autoplay
const urlTimeParam = location && location.href && location.href.indexOf('t=') > -1;
const forceAutoplayParam = location && location.state && location.state.forceAutoplay;
const autoplay = forceAutoplayParam || urlTimeParam || props.autoplay;
const isFree = costInfo && costInfo.cost === 0;
const canViewFile = isFree || claimWasPurchased;
2019-08-13 07:35:13 +02:00
const fileStatus = fileInfo && fileInfo.status;
const isPlayable = RENDER_MODES.FLOATING_MODES.includes(renderMode);
2021-03-11 18:08:11 +01:00
const isText = RENDER_MODES.TEXT_MODES.includes(renderMode);
const [thumbnail, setThumbnail] = React.useState(FileRenderPlaceholder);
const containerRef = React.useRef<any>();
useEffect(() => {
if (claimThumbnail) {
setTimeout(() => {
let newThumbnail = claimThumbnail;
// @if TARGET='web'
if (
containerRef.current &&
containerRef.current.parentElement &&
containerRef.current.parentElement.offsetWidth
) {
const w = containerRef.current.parentElement.offsetWidth;
newThumbnail = getThumbnailCdnUrl({ thumbnail: newThumbnail, width: w, height: w });
}
// @endif
if (newThumbnail !== thumbnail) {
setThumbnail(newThumbnail);
}
}, 200);
}
}, [claimThumbnail, thumbnail]);
2019-08-13 07:35:13 +02:00
2020-05-21 17:38:28 +02:00
function doAuthRedirect() {
history.push(`/$/${PAGES.AUTH}?redirect=${encodeURIComponent(location.pathname)}`);
}
2019-08-13 07:35:13 +02:00
// Wrap this in useCallback because we need to use it to the keyboard effect
2019-10-10 07:26:56 +02:00
// 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
2019-08-13 07:35:13 +02:00
const viewFile = useCallback(
(e?: SyntheticInputEvent<*> | KeyboardEvent) => {
if (e) {
e.stopPropagation();
}
play(uri, collectionId, isPlayable);
2019-08-13 07:35:13 +02:00
},
[play, uri, isPlayable, collectionId]
2019-08-13 07:35:13 +02:00
);
useEffect(() => {
// This is just for beginning to download a file
// Play/Pause/Fullscreen will be handled by the respective viewers because not every file type should behave the same
function handleKeyDown(e: KeyboardEvent) {
2021-08-31 09:05:42 +02:00
if (!isUserTyping() && e.keyCode === KEYCODES.SPACEBAR) {
2019-08-13 07:35:13 +02:00
e.preventDefault();
if (!isPlaying || fileStatus === 'stopped') {
viewFile(e);
}
}
}
window.addEventListener('keydown', handleKeyDown);
return () => {
window.removeEventListener('keydown', handleKeyDown);
};
}, [isPlaying, fileStatus, viewFile]);
useEffect(() => {
const videoOnPage = document.querySelector('video');
if (
(canViewFile || forceAutoplayParam) &&
((autoplay && (!videoOnPage || forceAutoplayParam) && isPlayable) ||
RENDER_MODES.AUTO_RENDER_MODES.includes(renderMode))
) {
2019-08-13 07:35:13 +02:00
viewFile();
}
}, [autoplay, canViewFile, forceAutoplayParam, isPlayable, renderMode, viewFile]);
/*
once content is playing, let the appropriate <FileRender> take care of it...
but for playables, always render so area can be used to fill with floating player
*/
if (isPlaying && !isPlayable) {
if (canViewFile && !collectionId) {
return null;
}
}
2020-05-21 17:38:28 +02:00
const showAppNag = IS_WEB && RENDER_MODES.UNSUPPORTED_IN_THIS_APP.includes(renderMode);
const disabled = showAppNag || (!fileInfo && insufficientCredits && !claimWasPurchased);
const shouldRedirect = IS_WEB && !authenticated && !isFree;
2019-08-13 07:35:13 +02:00
return (
<div
ref={containerRef}
2020-05-21 17:38:28 +02:00
onClick={disabled ? undefined : shouldRedirect ? doAuthRedirect : viewFile}
style={thumbnail && !obscurePreview ? { backgroundImage: `url("${thumbnail}")` } : {}}
className={classnames('content__cover', {
'content__cover--disabled': disabled,
2021-01-08 16:21:27 +01:00
'content__cover--theater-mode': videoTheaterMode,
2021-03-11 18:08:11 +01:00
'content__cover--text': isText,
2019-08-13 07:35:13 +02:00
'card__media--nsfw': obscurePreview,
})}
>
{showAppNag && (
<Nag
type="helpful"
inline
message={__('This content requires LBRY Desktop to display.')}
actionText={__('Get the App')}
href="https://lbry.com/get"
2019-09-02 15:24:00 +02:00
/>
)}
2020-05-21 17:38:28 +02:00
{!claimWasPurchased && insufficientCredits && !showAppNag && (
<Nag
type="helpful"
inline
2020-09-04 19:14:48 +02:00
message={__('You need more Credits to purchase this.')}
actionText={__('Open Rewards')}
onClick={() => history.push(`/$/${PAGES.REWARDS}`)}
/>
)}
{!disabled && (
2019-08-13 07:35:13 +02:00
<Button
2020-05-21 23:25:38 +02:00
requiresAuth={shouldRedirect}
2019-08-13 07:35:13 +02:00
onClick={viewFile}
iconSize={30}
title={isPlayable ? __('Play') : __('View')}
className={classnames('button--icon', {
'button--play': isPlayable,
'button--view': !isPlayable,
})}
/>
)}
</div>
);
}