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
2020-01-13 19:33:07 +01:00
import React , { useEffect , useCallback } from 'react' ;
2019-08-13 07:35:13 +02:00
import classnames from 'classnames' ;
2020-04-01 20:43:50 +02:00
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' ;
2021-06-25 06:15:17 +02:00
import { getThumbnailCdnUrl } from 'util/thumbnail' ;
2020-04-01 20:43:50 +02:00
import Nag from 'component/common/nag' ;
2021-06-25 06:15:17 +02:00
// $FlowFixMe cannot resolve ...
import FileRenderPlaceholder from 'static/img/fileRenderPlaceholder.png' ;
2019-08-13 07:35:13 +02:00
type Props = {
2021-09-13 17:24:35 +02:00
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 } ,
2021-09-10 19:27:21 +02:00
location : { search : ? string , pathname : string , href : string , state : { forceAutoplay : boolean } } ,
2019-08-13 07:35:13 +02:00
obscurePreview : boolean ,
insufficientCredits : boolean ,
2021-06-25 06:15:17 +02:00
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 ,
2020-04-01 20:43:50 +02:00
renderMode : string ,
2020-01-13 19:33:07 +01:00
claim : StreamClaim ,
2020-05-21 17:38:28 +02:00
claimWasPurchased : boolean ,
authenticated : boolean ,
2021-01-08 16:21:27 +01:00
videoTheaterMode : boolean ,
2021-09-02 22:05:32 +02:00
collectionId : string ,
2019-08-13 07:35:13 +02:00
} ;
2020-04-01 20:43:50 +02:00
export default function FileRenderInitiator ( props : Props ) {
2019-08-14 05:09:25 +02:00
const {
play ,
isPlaying ,
fileInfo ,
uri ,
obscurePreview ,
insufficientCredits ,
2020-04-01 20:43:50 +02:00
history ,
2020-05-21 17:38:28 +02:00
location ,
2021-06-25 06:15:17 +02:00
claimThumbnail ,
2020-04-01 20:43:50 +02:00
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 ,
2021-09-02 22:05:32 +02:00
collectionId ,
2019-08-14 05:09:25 +02:00
} = props ;
2021-05-15 07:59:21 +02:00
2021-09-10 19:27:21 +02:00
// 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 ;
2021-05-15 07:59:21 +02:00
2021-09-10 19:27:21 +02:00
const isFree = costInfo && costInfo . cost === 0 ;
const canViewFile = isFree || claimWasPurchased ;
2019-08-13 07:35:13 +02:00
const fileStatus = fileInfo && fileInfo . status ;
2020-04-01 20:43:50 +02:00
const isPlayable = RENDER _MODES . FLOATING _MODES . includes ( renderMode ) ;
2021-03-11 18:08:11 +01:00
const isText = RENDER _MODES . TEXT _MODES . includes ( renderMode ) ;
2021-06-25 06:15:17 +02:00
const [ thumbnail , setThumbnail ] = React . useState ( FileRenderPlaceholder ) ;
const containerRef = React . useRef < any > ( ) ;
2021-09-10 19:27:21 +02:00
useEffect ( ( ) => {
2021-06-25 06:15:17 +02:00
if ( claimThumbnail ) {
setTimeout ( ( ) => {
let newThumbnail = claimThumbnail ;
// @if TARGET='web'
if (
containerRef . current &&
containerRef . current . parentElement &&
containerRef . current . parentElement . offsetWidth
) {
2021-06-25 19:03:49 +02:00
const w = containerRef . current . parentElement . offsetWidth ;
newThumbnail = getThumbnailCdnUrl ( { thumbnail : newThumbnail , width : w , height : w } ) ;
2021-06-25 06:15:17 +02:00
}
// @endif
2021-06-25 19:03:49 +02:00
if ( newThumbnail !== thumbnail ) {
setThumbnail ( newThumbnail ) ;
}
2021-06-25 06:15:17 +02:00
} , 200 ) ;
}
2021-09-10 19:27:21 +02:00
} , [ 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 ( ) ;
}
2021-09-13 17:24:35 +02:00
play ( uri , collectionId , isPlayable ) ;
2019-08-13 07:35:13 +02:00
} ,
2021-09-13 17:24:35 +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' ) ;
2021-05-24 10:46:47 +02:00
if (
2021-09-10 19:27:21 +02:00
( canViewFile || forceAutoplayParam ) &&
( ( autoplay && ( ! videoOnPage || forceAutoplayParam ) && isPlayable ) ||
RENDER _MODES . AUTO _RENDER _MODES . includes ( renderMode ) )
2021-05-24 10:46:47 +02:00
) {
2019-08-13 07:35:13 +02:00
viewFile ( ) ;
}
2021-09-10 19:27:21 +02:00
} , [ autoplay , canViewFile , forceAutoplayParam , isPlayable , renderMode , viewFile ] ) ;
2020-04-01 20:43:50 +02:00
/ *
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 ) {
2021-09-10 19:27:21 +02:00
if ( canViewFile && ! collectionId ) {
2021-05-24 10:52:30 +02:00
return null ;
}
2020-04-01 20:43:50 +02:00
}
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
2021-06-25 06:15:17 +02:00
ref = { containerRef }
2020-05-21 17:38:28 +02:00
onClick = { disabled ? undefined : shouldRedirect ? doAuthRedirect : viewFile }
2021-06-25 06:15:17 +02:00
style = { thumbnail && ! obscurePreview ? { backgroundImage : ` url(" ${ thumbnail } ") ` } : { } }
2020-04-01 20:43:50 +02:00
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 ,
} ) }
>
2020-04-01 20:43:50 +02:00
{ 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 && (
2020-04-01 20:43:50 +02:00
< Nag
type = "helpful"
inline
2020-09-04 19:14:48 +02:00
message = { _ _ ( 'You need more Credits to purchase this.' ) }
2020-04-01 20:43:50 +02:00
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 >
) ;
}