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

165 lines
5.1 KiB
React
Raw Normal View History

2019-08-13 01:35:13 -04:00
// @flow
// This component is entirely for triggering the start of a file view
2020-01-06 13:32:35 -05:00
// The actual viewer for a file exists in TextViewer and FloatingViewer
2019-08-13 01:35:13 -04: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 01:35:13 -04:00
import classnames from 'classnames';
import Button from 'component/button';
import isUserTyping from 'util/detect-typing';
2019-09-02 09:24:00 -04:00
import Yrbl from 'component/yrbl';
import I18nMessage from 'component/i18nMessage';
2019-08-13 01:35:13 -04:00
const SPACE_BAR_KEYCODE = 32;
type Props = {
play: string => void,
mediaType: string,
2020-01-06 15:57:49 -05:00
isText: boolean,
contentType: string,
2019-08-13 01:35:13 -04:00
isLoading: boolean,
isPlaying: boolean,
fileInfo: FileListItem,
uri: string,
obscurePreview: boolean,
insufficientCredits: boolean,
isStreamable: boolean,
thumbnail?: string,
autoplay: boolean,
hasCostInfo: boolean,
2019-08-28 19:33:38 -04:00
costInfo: any,
isAutoPlayable: boolean,
2020-01-06 13:32:35 -05:00
inline: boolean,
claim: StreamClaim,
2019-08-13 01:35:13 -04:00
};
2020-01-06 13:32:35 -05:00
export default function FileViewerInitiator(props: Props) {
2019-08-13 23:09:25 -04:00
const {
play,
mediaType,
2020-01-06 15:57:49 -05:00
isText,
contentType,
2019-08-13 23:09:25 -04:00
isPlaying,
fileInfo,
uri,
obscurePreview,
insufficientCredits,
thumbnail,
autoplay,
isStreamable,
hasCostInfo,
2019-08-28 19:33:38 -04:00
costInfo,
isAutoPlayable,
claim,
2019-08-13 23:09:25 -04:00
} = props;
const cost = costInfo && costInfo.cost;
const forceVideo = ['application/x-ext-mkv', 'video/x-matroska'].includes(contentType);
const isPlayable = ['audio', 'video'].includes(mediaType) || forceVideo;
const isImage = mediaType === 'image';
2019-08-13 01:35:13 -04:00
const fileStatus = fileInfo && fileInfo.status;
const webStreamOnly = contentType === 'application/pdf' || mediaType === 'text';
2019-12-15 23:02:23 -05:00
const supported = IS_WEB ? (!cost && isStreamable) || webStreamOnly || forceVideo : true;
const { name, claim_id: claimId, value } = claim;
const fileName = value && value.source && value.source.name;
const downloadUrl = `/$/download/${name}/${claimId}`;
2019-12-15 23:02:23 -05:00
function getTitle() {
let message = __('Unsupported File');
// @if TARGET='web'
if (cost) {
message = __('Paid Content Not Supported on lbry.tv');
} else {
message = __("We're not quite ready to display this file on lbry.tv yet");
2019-12-15 23:02:23 -05:00
}
// @endif
2019-12-15 23:02:23 -05:00
return message;
2019-12-15 23:02:23 -05:00
}
2019-08-13 01:35:13 -04:00
// Wrap this in useCallback because we need to use it to the keyboard effect
2019-10-10 10:56:56 +05:30
// 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 01:35:13 -04:00
const viewFile = useCallback(
(e?: SyntheticInputEvent<*> | KeyboardEvent) => {
if (e) {
e.stopPropagation();
}
play(uri);
},
[play, uri]
);
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) {
if (!isUserTyping() && e.keyCode === SPACE_BAR_KEYCODE) {
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 (((autoplay && !videoOnPage && isAutoPlayable) || isText || isImage) && hasCostInfo && cost === 0) {
2019-08-13 01:35:13 -04:00
viewFile();
}
2020-01-06 13:32:35 -05:00
}, [autoplay, viewFile, isAutoPlayable, hasCostInfo, cost, isText]);
2019-08-13 01:35:13 -04:00
return (
<div
disabled={!hasCostInfo}
2019-12-19 15:43:49 -05:00
style={!obscurePreview && supported && thumbnail && !isPlaying ? { backgroundImage: `url("${thumbnail}")` } : {}}
onClick={supported ? viewFile : undefined}
2019-09-02 09:24:00 -04:00
className={classnames({
content__cover: supported,
'content__cover--disabled': !supported,
2020-01-06 13:32:35 -05:00
'content__cover--hidden-for-text': isText,
2019-08-13 01:35:13 -04:00
'card__media--nsfw': obscurePreview,
'card__media--disabled': supported && !fileInfo && insufficientCredits,
2019-08-13 01:35:13 -04:00
})}
>
2019-09-02 09:24:00 -04:00
{!supported && (
<Yrbl
type="happy"
title={getTitle()}
2019-09-02 09:24:00 -04:00
subtitle={
<I18nMessage
tokens={{
download_the_app: <Button button="link" label={__('download the app')} href="https://lbry.com/get" />,
download_this_file: (
<Button button="link" label={__('download this file')} download={fileName} href={downloadUrl} />
),
}}
>
Good news, though! You can %download_the_app% and gain access to everything, or %download_this_file% and
view it on your device.
</I18nMessage>
2019-09-02 09:24:00 -04:00
}
/>
)}
2020-01-06 13:32:35 -05:00
2019-09-02 09:24:00 -04:00
{!isPlaying && supported && (
2019-08-13 01:35:13 -04:00
<Button
onClick={viewFile}
iconSize={30}
title={isPlayable ? __('Play') : __('View')}
className={classnames('button--icon', {
'button--play': isPlayable,
'button--view': !isPlayable,
})}
/>
)}
</div>
);
}