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

260 lines
8.6 KiB
React
Raw Normal View History

2019-06-11 20:10:58 +02:00
// @flow
import type { Node } from 'react';
2019-08-02 08:28:14 +02:00
import React, { Fragment, useEffect, forwardRef } from 'react';
2019-06-11 20:10:58 +02:00
import classnames from 'classnames';
2019-06-27 08:18:45 +02:00
import { parseURI, convertToShareLink } from 'lbry-redux';
2019-06-11 20:10:58 +02:00
import { withRouter } from 'react-router-dom';
import { openCopyLinkMenu } from 'util/context-menu';
import { formatLbryUrlForWeb } from 'util/url';
2019-08-25 13:42:25 +02:00
import { isEmpty } from 'util/object';
2019-06-11 20:10:58 +02:00
import CardMedia from 'component/cardMedia';
import UriIndicator from 'component/uriIndicator';
import TruncatedText from 'component/common/truncated-text';
import DateTime from 'component/dateTime';
import FileProperties from 'component/fileProperties';
2019-06-19 07:05:43 +02:00
import ClaimTags from 'component/claimTags';
2019-06-11 20:10:58 +02:00
import SubscribeButton from 'component/subscribeButton';
import ChannelThumbnail from 'component/channelThumbnail';
import BlockButton from 'component/blockButton';
2019-07-21 23:31:22 +02:00
import Button from 'component/button';
2019-06-11 20:10:58 +02:00
type Props = {
uri: string,
claim: ?Claim,
obscureNsfw: boolean,
showUserBlocked: boolean,
2019-06-11 20:10:58 +02:00
claimIsMine: boolean,
pending?: boolean,
resolveUri: string => void,
isResolvingUri: boolean,
history: { push: string => void },
thumbnail: string,
title: string,
nsfw: boolean,
2019-07-21 23:31:22 +02:00
placeholder: string,
2019-06-19 07:05:43 +02:00
type: string,
2019-07-11 20:06:25 +02:00
hasVisitedUri: boolean,
blackListedOutpoints: Array<{
txid: string,
nout: number,
}>,
filteredOutpoints: Array<{
txid: string,
nout: number,
}>,
blockedChannelUris: Array<string>,
channelIsBlocked: boolean,
isSubscribed: boolean,
beginPublish: string => void,
actions: boolean | Node | string | number,
properties: boolean | Node | string | number,
onClick?: any => any,
2019-11-22 22:13:00 +01:00
hideBlock?: boolean,
2019-06-11 20:10:58 +02:00
};
2019-08-02 08:28:14 +02:00
const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
2019-06-11 20:10:58 +02:00
const {
obscureNsfw,
claimIsMine,
pending,
history,
uri,
isResolvingUri,
thumbnail,
title,
nsfw,
resolveUri,
claim,
placeholder,
2019-06-19 07:05:43 +02:00
type,
blackListedOutpoints,
filteredOutpoints,
blockedChannelUris,
2019-07-11 20:06:25 +02:00
hasVisitedUri,
showUserBlocked,
channelIsBlocked,
isSubscribed,
beginPublish,
actions,
properties,
onClick,
2019-11-22 22:13:00 +01:00
hideBlock,
2019-06-11 20:10:58 +02:00
} = props;
2019-10-03 23:40:54 +02:00
const shouldFetch =
claim === undefined || (claim !== null && claim.value_type === 'channel' && isEmpty(claim.meta) && !pending);
2019-07-21 23:31:22 +02:00
const abandoned = !isResolvingUri && !claim;
2019-06-11 20:10:58 +02:00
const claimsInChannel = (claim && claim.meta.claims_in_channel) || 0;
2019-07-21 23:31:22 +02:00
const showPublishLink = abandoned && placeholder === 'publish';
2019-07-23 10:05:51 +02:00
const hideActions = type === 'small' || type === 'tooltip';
2019-07-21 23:31:22 +02:00
let name;
let isValid = false;
if (uri) {
try {
({ streamName: name } = parseURI(uri));
isValid = true;
} catch (e) {
isValid = false;
}
2019-07-11 21:03:31 +02:00
}
2019-07-05 20:11:55 +02:00
2019-07-11 21:03:31 +02:00
const isChannel = isValid ? parseURI(uri).isChannel : false;
const includeChannelTooltip = type !== 'inline' && type !== 'tooltip' && !isChannel;
2019-07-29 16:12:53 +02:00
const signingChannel = claim && claim.signing_channel;
// do not block abandoned and nsfw claims if showUserBlocked is passed
2019-10-17 18:58:28 +02:00
let shouldHide =
placeholder !== 'loading' &&
!showUserBlocked &&
((abandoned && !showPublishLink) || (!claimIsMine && obscureNsfw && nsfw));
2019-07-05 20:11:55 +02:00
// This will be replaced once blocking is done at the wallet server level
if (claim && !shouldHide && blackListedOutpoints) {
shouldHide = blackListedOutpoints.some(
outpoint =>
(signingChannel && outpoint.txid === signingChannel.txid && outpoint.nout === signingChannel.nout) ||
(outpoint.txid === claim.txid && outpoint.nout === claim.nout)
);
}
// We're checking to see if the stream outpoint
// or signing channel outpoint is in the filter list
if (claim && !shouldHide && filteredOutpoints) {
shouldHide = filteredOutpoints.some(
outpoint =>
(signingChannel && outpoint.txid === signingChannel.txid && outpoint.nout === signingChannel.nout) ||
(outpoint.txid === claim.txid && outpoint.nout === claim.nout)
);
2019-07-05 20:11:55 +02:00
}
// block stream claims
if (claim && !shouldHide && !showUserBlocked && blockedChannelUris.length && signingChannel) {
shouldHide = blockedChannelUris.some(blockedUri => blockedUri === signingChannel.permanent_url);
}
// block channel claims if we can't control for them in claim search
2019-08-02 17:11:31 +02:00
// e.g. fetchRecommendedSubscriptions
2019-10-08 18:25:33 +02:00
if (claim && isChannel && !shouldHide && !showUserBlocked && blockedChannelUris.length) {
shouldHide = blockedChannelUris.some(blockedUri => blockedUri === claim.permanent_url);
}
2019-06-11 20:10:58 +02:00
function handleContextMenu(e) {
// @if TARGET='app'
2019-06-11 20:10:58 +02:00
e.preventDefault();
e.stopPropagation();
if (claim) {
2019-11-02 16:03:21 +01:00
openCopyLinkMenu(convertToShareLink(claim.canonical_url || claim.permanent_url), e);
2019-06-11 20:10:58 +02:00
}
// @endif
2019-06-11 20:10:58 +02:00
}
function handleOnClick(e) {
if (onClick) {
onClick(e);
} else if ((isChannel || title) && !pending) {
history.push(formatLbryUrlForWeb(claim && claim.canonical_url ? claim.canonical_url : uri));
2019-06-11 20:10:58 +02:00
}
}
useEffect(() => {
2019-08-25 13:42:25 +02:00
if (isValid && !isResolvingUri && shouldFetch && uri) {
2019-06-11 20:10:58 +02:00
resolveUri(uri);
}
2019-08-25 13:42:25 +02:00
}, [isValid, isResolvingUri, uri, resolveUri, shouldFetch]);
2019-06-11 20:10:58 +02:00
if (shouldHide) {
return null;
}
2019-07-21 23:31:22 +02:00
if (placeholder === 'loading' || (isResolvingUri && !claim)) {
2019-06-11 20:10:58 +02:00
return (
2019-07-21 23:31:22 +02:00
<li className={classnames('claim-preview', { 'claim-preview--large': type === 'large' })} disabled>
2019-06-11 20:10:58 +02:00
<div className="placeholder media__thumb" />
<div className="placeholder__wrapper">
2019-06-27 08:18:45 +02:00
<div className="placeholder claim-preview-title" />
2019-06-11 20:10:58 +02:00
<div className="placeholder media__subtitle" />
</div>
</li>
);
}
return (
<li
2019-08-02 08:28:14 +02:00
ref={ref}
2019-06-11 20:10:58 +02:00
role="link"
onClick={pending || type === 'inline' ? undefined : handleOnClick}
2019-06-11 20:10:58 +02:00
onContextMenu={handleContextMenu}
2019-06-27 08:18:45 +02:00
className={classnames('claim-preview', {
2019-07-23 10:05:51 +02:00
'claim-preview--small': type === 'small' || type === 'tooltip',
2019-06-27 08:18:45 +02:00
'claim-preview--large': type === 'large',
2019-07-21 23:31:22 +02:00
'claim-preview--inline': type === 'inline',
2019-07-23 10:05:51 +02:00
'claim-preview--tooltip': type === 'tooltip',
2019-11-22 22:13:00 +01:00
'claim-preview--channel': isChannel,
2019-07-23 10:05:51 +02:00
'claim-preview--visited': !isChannel && !claimIsMine && hasVisitedUri,
2019-07-11 20:06:25 +02:00
'claim-preview--pending': pending,
2019-06-11 20:10:58 +02:00
})}
>
{isChannel ? <ChannelThumbnail uri={uri} obscure={channelIsBlocked} /> : <CardMedia thumbnail={thumbnail} />}
2019-06-27 08:18:45 +02:00
<div className="claim-preview-metadata">
<div className="claim-preview-info">
<div className="claim-preview-title">
2019-07-21 23:31:22 +02:00
{claim ? <TruncatedText text={title || claim.name} lines={1} /> : <span>{__('Nothing here')}</span>}
2019-06-11 20:10:58 +02:00
</div>
2019-10-03 23:40:54 +02:00
{!pending && (
<React.Fragment>
2019-10-15 06:26:40 +02:00
{hideActions ? null : actions !== undefined ? (
2019-10-03 23:40:54 +02:00
actions
) : (
<div className="card__actions--inline">
{isChannel && !channelIsBlocked && !claimIsMine && (
<SubscribeButton uri={uri.startsWith('lbry://') ? uri : `lbry://${uri}`} />
)}
2019-11-22 22:13:00 +01:00
{!hideBlock && isChannel && !isSubscribed && !claimIsMine && (
2019-10-03 23:40:54 +02:00
<BlockButton uri={uri.startsWith('lbry://') ? uri : `lbry://${uri}`} />
)}
{!isChannel && claim && <FileProperties uri={uri} />}
</div>
2019-09-27 20:56:15 +02:00
)}
2019-10-03 23:40:54 +02:00
</React.Fragment>
2019-09-27 20:56:15 +02:00
)}
2019-06-11 20:10:58 +02:00
</div>
2019-06-27 08:18:45 +02:00
<div className="claim-preview-properties">
2019-06-11 20:10:58 +02:00
<div className="media__subtitle">
2019-07-21 23:31:22 +02:00
{!isResolvingUri && (
<div>
{claim ? (
2019-07-23 10:05:51 +02:00
<UriIndicator uri={uri} link addTooltip={includeChannelTooltip} />
2019-07-21 23:31:22 +02:00
) : (
<Fragment>
<div>{__('Publish something and claim this spot!')}</div>
<div className="card__actions">
<Button
onClick={() => beginPublish(name)}
button="primary"
label={`${__('Publish to')} ${uri}`}
/>
2019-07-21 23:31:22 +02:00
</div>
</Fragment>
)}
<div>
2019-10-03 23:40:54 +02:00
{pending ? (
<div>{__('Pending...')}</div>
2019-10-03 23:40:54 +02:00
) : (
claim &&
2019-09-04 06:22:31 +02:00
(isChannel ? (
type !== 'inline' && `${claimsInChannel} ${__('publishes')}`
) : (
<DateTime timeAgo uri={uri} />
2019-10-03 23:40:54 +02:00
))
)}
2019-07-21 23:31:22 +02:00
</div>
</div>
)}
2019-06-11 20:10:58 +02:00
</div>
{properties !== undefined ? properties : <ClaimTags uri={uri} type={type} />}
2019-06-11 20:10:58 +02:00
</div>
</div>
</li>
);
2019-08-02 08:28:14 +02:00
});
2019-06-11 20:10:58 +02:00
export default withRouter(ClaimPreview);