lbry-desktop/src/ui/page/file/view.jsx

308 lines
9.6 KiB
React
Raw Normal View History

2018-03-26 23:32:43 +02:00
// @flow
2018-11-26 02:21:25 +01:00
import * as MODALS from 'constants/modal_types';
import * as icons from 'constants/icons';
2018-03-26 23:32:43 +02:00
import * as React from 'react';
2019-05-13 08:37:34 +02:00
import { clipboard } from 'electron';
import { buildURI, normalizeURI } from 'lbry-redux';
2018-07-10 05:32:59 +02:00
import FileViewer from 'component/fileViewer';
2018-03-26 23:32:43 +02:00
import Thumbnail from 'component/common/thumbnail';
import FilePrice from 'component/filePrice';
import FileDetails from 'component/fileDetails';
2018-03-26 23:32:43 +02:00
import FileActions from 'component/fileActions';
import UriIndicator from 'component/uriIndicator';
2018-03-26 23:32:43 +02:00
import Icon from 'component/common/icon';
import DateTime from 'component/dateTime';
2018-03-26 23:32:43 +02:00
import Button from 'component/button';
import SubscribeButton from 'component/subscribeButton';
2018-03-26 23:32:43 +02:00
import Page from 'component/page';
2018-05-21 22:30:00 +02:00
import FileDownloadLink from 'component/fileDownloadLink';
import classnames from 'classnames';
2018-11-21 22:20:55 +01:00
import getMediaType from 'util/get-media-type';
import RecommendedContent from 'component/recommendedContent';
2018-03-26 23:32:43 +02:00
type Props = {
2019-04-24 16:02:08 +02:00
claim: StreamClaim,
fileInfo: FileListItem,
2018-03-26 23:32:43 +02:00
contentType: string,
uri: string,
rewardedContentClaimIds: Array<string>,
obscureNsfw: boolean,
claimIsMine: boolean,
costInfo: ?{ cost: number },
2018-03-26 23:32:43 +02:00
fetchFileInfo: string => void,
fetchCostInfo: string => void,
setViewed: string => void,
2018-10-19 22:38:07 +02:00
isSubscribed: ?string,
isSubscribed: boolean,
channelUri: string,
2019-03-14 19:40:26 +01:00
viewCount: number,
2018-10-19 22:38:07 +02:00
prepareEdit: ({}, string) => void,
openModal: (id: string, { uri: string }) => void,
2018-10-19 22:38:07 +02:00
markSubscriptionRead: (string, string) => void,
2019-03-14 19:40:26 +01:00
fetchViewCount: string => void,
balance: number,
2019-04-24 16:02:08 +02:00
title: string,
thumbnail: ?string,
nsfw: boolean,
2019-05-13 08:37:34 +02:00
showToast: ({}) => void,
2018-03-26 23:32:43 +02:00
};
class FilePage extends React.Component<Props> {
static PLAYABLE_MEDIA_TYPES = ['audio', 'video'];
static PREVIEW_MEDIA_TYPES = [
'text',
'model',
'image',
'script',
'document',
'3D-file',
2019-05-09 00:47:39 +02:00
'comic-book',
// Bypass unplayable files
// TODO: Find a better way to detect supported types
'application',
];
2018-06-11 08:41:25 +02:00
2017-05-13 00:50:51 +02:00
componentDidMount() {
2019-03-14 19:40:26 +01:00
const {
uri,
fetchFileInfo,
fetchCostInfo,
setViewed,
isSubscribed,
claimIsMine,
fetchViewCount,
claim,
} = this.props;
2018-10-19 22:38:07 +02:00
if (isSubscribed) {
2018-10-19 22:38:07 +02:00
this.removeFromSubscriptionNotifications();
}
2019-03-14 19:40:26 +01:00
if (claimIsMine) {
2019-03-14 19:40:26 +01:00
fetchViewCount(claim.claim_id);
}
2019-03-15 17:15:31 +01:00
// always refresh file info when entering file page to see if we have the file
// @if TARGET='app'
fetchFileInfo(uri);
2019-03-15 17:15:31 +01:00
// @endif
2017-05-12 19:14:06 +02:00
// See https://github.com/lbryio/lbry-desktop/pull/1563 for discussion
fetchCostInfo(uri);
setViewed(uri);
2017-05-12 19:14:06 +02:00
}
2018-10-19 22:38:07 +02:00
componentDidUpdate(prevProps: Props) {
const { isSubscribed, claim, uri, fileInfo, setViewed, fetchViewCount, claimIsMine, fetchFileInfo } = this.props;
2019-03-14 19:40:26 +01:00
if (!prevProps.isSubscribed && isSubscribed) {
2018-10-19 22:38:07 +02:00
this.removeFromSubscriptionNotifications();
}
2019-03-14 19:40:26 +01:00
if (prevProps.uri !== uri && claimIsMine) {
2019-03-14 19:40:26 +01:00
fetchViewCount(claim.claim_id);
}
// @if TARGET='app'
if (fileInfo === undefined) {
fetchFileInfo(uri);
}
// @endif
if (prevProps.uri !== uri) {
setViewed(uri);
}
2018-10-19 22:38:07 +02:00
}
removeFromSubscriptionNotifications() {
// Always try to remove
// If it doesn't exist, nothing will happen
const { markSubscriptionRead, uri, channelUri } = this.props;
2018-10-19 22:38:07 +02:00
markSubscriptionRead(channelUri, uri);
}
2017-05-12 19:14:06 +02:00
render() {
const {
claim,
contentType,
uri,
2017-07-30 00:56:08 +02:00
rewardedContentClaimIds,
2018-03-26 23:32:43 +02:00
obscureNsfw,
openModal,
claimIsMine,
prepareEdit,
costInfo,
fileInfo,
2018-10-19 22:38:07 +02:00
channelUri,
2019-03-14 19:40:26 +01:00
viewCount,
balance,
2019-04-24 16:02:08 +02:00
title,
thumbnail,
nsfw,
2019-05-13 08:37:34 +02:00
showToast,
} = this.props;
2017-05-01 22:50:07 +02:00
2018-03-26 23:32:43 +02:00
// File info
2019-05-13 08:37:34 +02:00
const { channel_name: channelName } = claim;
const { PLAYABLE_MEDIA_TYPES, PREVIEW_MEDIA_TYPES } = FilePage;
const isRewardContent = (rewardedContentClaimIds || []).includes(claim.claim_id);
2019-04-24 16:02:08 +02:00
const shouldObscureThumbnail = obscureNsfw && nsfw;
const fileName = fileInfo ? fileInfo.file_name : null;
2018-07-10 04:57:34 +02:00
const mediaType = getMediaType(contentType, fileName);
2019-05-26 08:18:47 +02:00
const isPreviewType = PREVIEW_MEDIA_TYPES.includes(mediaType);
const isPlayableType = PLAYABLE_MEDIA_TYPES.includes(mediaType);
const showFile = isPlayableType || isPreviewType;
2018-10-04 15:39:14 +02:00
const speechShareable =
2019-05-07 23:38:29 +02:00
costInfo && costInfo.cost === 0 && contentType && ['video', 'image'].includes(contentType.split('/')[0]);
// We want to use the short form uri for editing
// This is what the user is used to seeing, they don't care about the claim id
// We will select the claim id before they publish
let editUri;
if (claimIsMine) {
const uriObject: { contentName: string, claimId: string, channelName?: string } = {
2018-10-19 22:38:07 +02:00
contentName: claim.name,
claimId: claim.claim_id,
};
if (channelName) {
2018-06-12 07:11:17 +02:00
uriObject.channelName = channelName;
}
editUri = buildURI(uriObject);
}
const insufficientCredits = !claimIsMine && costInfo && costInfo.cost > balance;
2017-05-12 19:14:06 +02:00
return (
2019-01-09 19:39:05 +01:00
<Page notContained className="main--file-page">
<div className="grid-area--content">
2019-05-13 08:37:34 +02:00
<Button
className="media__uri"
button="alt"
label={uri}
onClick={() => {
clipboard.writeText(uri);
showToast({
message: __('Text copied'),
});
}}
/>
{!fileInfo && insufficientCredits && (
<div className="media__insufficient-credits help--warning">
{__(
'The publisher has chosen to charge LBC to view this content. Your balance is currently to low to view it.'
)}{' '}
2019-05-07 23:38:29 +02:00
{__('Checkout')} <Button button="link" navigate="/$/rewards" label={__('the rewards page')} />{' '}
{__('or send more LBC to your wallet.')}
</div>
)}
{showFile && (
<FileViewer
insufficientCredits={insufficientCredits}
className="content__embedded"
uri={uri}
mediaType={mediaType}
2019-05-26 08:18:47 +02:00
isPlayableType={isPlayableType}
/>
)}
2019-01-09 19:39:05 +01:00
{!showFile &&
(thumbnail ? (
<Thumbnail shouldObscure={shouldObscureThumbnail} src={thumbnail} />
) : (
<div
className={classnames('content__empty', {
'content__empty--nsfw': shouldObscureThumbnail,
})}
>
2019-05-07 23:38:29 +02:00
<div className="card__media-text">{__("Sorry, looks like we can't preview this file.")}</div>
</div>
))}
2019-01-09 19:39:05 +01:00
</div>
2019-01-09 19:39:05 +01:00
<div className="grid-area--info media__content media__content--large">
<h1 className="media__title media__title--large">{title}</h1>
<div className="media__properties media__properties--large">
{isRewardContent && (
<Icon
size={20}
iconColor="red"
icon={icons.FEATURED}
// Figure out how to get the tooltip to overlap the navbar on the file page and I will love you
// https://stackoverflow.com/questions/6421966/css-overflow-x-visible-and-overflow-y-hidden-causing-scrollbar-issue
// https://spee.ch/4/overflow-issue
// tooltip="bottom"
/>
)}
2019-04-24 16:02:08 +02:00
{nsfw && <div className="badge badge--nsfw">MATURE</div>}
<FilePrice badge uri={normalizeURI(uri)} />
</div>
2019-03-14 19:40:26 +01:00
<div className="media__actions media__actions--between">
<div className="media__subtext media__subtext--large">
<div className="media__subtitle__channel">
<UriIndicator uri={uri} link />
</div>
{__('Published on')} <DateTime uri={uri} show={DateTime.SHOW_DATE} />
2019-03-14 19:40:26 +01:00
</div>
2019-03-15 16:31:50 +01:00
{claimIsMine && (
<div className="media__subtext--large">
{viewCount} {viewCount !== 1 ? __('Views') : __('View')}
</div>
)}
</div>
<div className="media__actions media__actions--between">
<div className="media__action-group--large">
2019-03-19 16:59:29 +01:00
{claimIsMine && (
<Button
button="primary"
icon={icons.EDIT}
label={__('Edit')}
2019-03-28 17:53:13 +01:00
navigate="/$/publish"
onClick={() => {
prepareEdit(claim, editUri);
}}
/>
)}
{!claimIsMine && (
2019-03-19 16:59:29 +01:00
<React.Fragment>
{channelUri && <SubscribeButton uri={channelUri} channelName={channelName} />}
<Button
button="alt"
icon={icons.TIP}
label={__('Send a tip')}
onClick={() => openModal(MODALS.SEND_TIP, { uri })}
2019-03-19 16:59:29 +01:00
/>
</React.Fragment>
)}
<Button
button="alt"
2019-01-22 21:36:28 +01:00
icon={icons.SHARE}
label={__('Share')}
onClick={() => openModal(MODALS.SOCIAL_SHARE, { uri, speechShareable })}
/>
2018-03-26 23:32:43 +02:00
</div>
<div className="media__action-group--large">
<FileDownloadLink uri={uri} />
2019-05-26 08:18:47 +02:00
<FileActions uri={uri} claimId={claim.claim_id} showFullscreen={isPreviewType} />
</div>
</div>
<div className="media__info--large">
<FileDetails uri={uri} />
</div>
</div>
2019-01-09 19:39:05 +01:00
<div className="grid-area--related">
<RecommendedContent uri={uri} />
</div>
2018-03-26 23:32:43 +02:00
</Page>
2017-06-06 23:19:12 +02:00
);
2017-05-12 19:14:06 +02:00
}
2017-05-05 07:10:37 +02:00
}
2017-05-01 21:59:40 +02:00
export default FilePage;