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

211 lines
7 KiB
React
Raw 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 { useIsMobile } from 'effects/use-screensize';
import React 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';
2019-08-13 07:35:13 +02:00
import Button from 'component/button';
import Nag from 'component/common/nag';
import * as COLLECTIONS_CONSTS from 'constants/collections';
2022-03-21 15:11:26 +01:00
import { LivestreamContext } from 'page/livestream/view';
import { formatLbryUrlForWeb } from 'util/url';
import FileViewerEmbeddedTitle from 'component/fileViewerEmbeddedTitle';
2022-03-16 18:35:27 +01:00
import useFetchLiveStatus from 'effects/use-fetch-live';
2022-03-17 11:53:18 +01:00
import useThumbnail from 'effects/use-thumbnail';
2019-08-13 07:35:13 +02:00
type Props = {
channelClaimId: ?string,
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,
2020-05-21 17:38:28 +02:00
claimWasPurchased: boolean,
authenticated: boolean,
2021-01-08 16:21:27 +01:00
videoTheaterMode: boolean,
isCurrentClaimLive?: boolean,
2022-03-15 17:18:08 +01:00
isLivestreamClaim: boolean,
customAction?: any,
embedded?: boolean,
parentCommentId?: string,
isMarkdownPost?: boolean,
doUriInitiatePlay: (playingOptions: PlayingUri, isPlayable: boolean) => void,
doFetchChannelLiveStatus: (string) => void,
2019-08-13 07:35:13 +02:00
};
export default function FileRenderInitiator(props: Props) {
2019-08-14 05:09:25 +02:00
const {
channelClaimId,
2019-08-14 05:09:25 +02:00
isPlaying,
fileInfo,
uri,
obscurePreview,
insufficientCredits,
history,
2020-05-21 17:38:28 +02:00
location,
claimThumbnail,
autoplay,
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,
isCurrentClaimLive,
2022-03-15 17:18:08 +01:00
isLivestreamClaim,
customAction,
embedded,
parentCommentId,
isMarkdownPost,
doUriInitiatePlay,
doFetchChannelLiveStatus,
2019-08-14 05:09:25 +02:00
} = props;
2022-03-21 15:11:26 +01:00
const { livestreamPage, layountRendered } = React.useContext(LivestreamContext) || {};
2022-03-16 22:39:09 +01:00
const isMobile = useIsMobile();
2022-03-15 17:18:08 +01:00
const containerRef = React.useRef<any>();
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;
2022-03-16 22:39:09 +01:00
const canViewFile = isLivestreamClaim
? (layountRendered || isMobile) && isCurrentClaimLive
: isFree || claimWasPurchased;
const isPlayable = RENDER_MODES.FLOATING_MODES.includes(renderMode) || isCurrentClaimLive;
2021-03-11 18:08:11 +01:00
const isText = RENDER_MODES.TEXT_MODES.includes(renderMode);
const renderUnsupported = RENDER_MODES.UNSUPPORTED_IN_THIS_APP.includes(renderMode);
2022-03-15 17:18:08 +01:00
const disabled =
(isLivestreamClaim && !isCurrentClaimLive) ||
renderUnsupported ||
(!fileInfo && insufficientCredits && !claimWasPurchased);
const shouldRedirect = !authenticated && !isFree;
2019-08-13 07:35:13 +02:00
2020-05-21 17:38:28 +02:00
function doAuthRedirect() {
history.push(`/$/${PAGES.AUTH}?redirect=${encodeURIComponent(pathname)}`);
2020-05-21 17:38:28 +02:00
}
2022-03-21 15:11:26 +01:00
useFetchLiveStatus(livestreamPage ? undefined : channelClaimId, doFetchChannelLiveStatus);
2022-03-17 11:53:18 +01:00
const thumbnail = useThumbnail(claimThumbnail, containerRef);
2019-08-13 07:35:13 +02:00
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]);
2019-08-13 07:35:13 +02:00
React.useEffect(() => {
2019-08-13 07:35:13 +02:00
const videoOnPage = document.querySelector('video');
if (
(canViewFile || forceAutoplayParam) &&
((shouldAutoplay && (!videoOnPage || forceAutoplayParam) && isPlayable) ||
2022-03-15 19:38:42 +01:00
(!embedded && RENDER_MODES.AUTO_RENDER_MODES.includes(renderMode)))
) {
2019-08-13 07:35:13 +02:00
viewFile();
}
2022-03-15 19:38:42 +01:00
}, [canViewFile, embedded, forceAutoplayParam, isPlayable, renderMode, shouldAutoplay, 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 && canViewFile && !collectionId) {
return null;
}
2019-08-13 07:35:13 +02:00
return (
<div
ref={containerRef}
onClick={disabled ? undefined : shouldRedirect ? doAuthRedirect : handleClick}
style={thumbnail && !obscurePreview ? { backgroundImage: `url("${thumbnail}")` } : {}}
className={
embedded
? 'embed__inline-button'
: classnames('content__cover', {
'content__cover--disabled': disabled,
'content__cover--theater-mode': videoTheaterMode && !isMobile,
'content__cover--text': isText,
'card__media--nsfw': obscurePreview,
})
}
2019-08-13 07:35:13 +02:00
>
{embedded && <FileViewerEmbeddedTitle uri={uri} isInApp />}
{renderUnsupported ? (
<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
/>
) : (
!claimWasPurchased &&
insufficientCredits && (
<Nag
type="helpful"
inline
message={__('You need more Credits to purchase this.')}
actionText={__('Open Rewards')}
onClick={() => history.push(`/$/${PAGES.REWARDS}`)}
/>
)
2019-09-02 15:24:00 +02:00
)}
{(!disabled || (embedded && isLivestreamClaim)) && (
2019-08-13 07:35:13 +02:00
<Button
2020-05-21 23:25:38 +02:00
requiresAuth={shouldRedirect}
onClick={handleClick}
2019-08-13 07:35:13 +02:00
iconSize={30}
title={isPlayable ? __('Play') : __('View')}
className={classnames('button--icon', {
'button--play': isPlayable,
'button--view': !isPlayable,
})}
/>
)}
2022-03-15 17:18:08 +01:00
{customAction}
2019-08-13 07:35:13 +02:00
</div>
);
}