lbry-desktop/ui/component/claimPreviewTile/view.jsx
Rave | 図書館猫 ffdb5abf63
Tile Grid Revamp (#1502)
* Save

* Save

* Add pulse

* Adjust footer ad

* Adjust tile ad

* Adjust tile ad hover

* Fix premium badge alignment in tile grid

* Adjust livestream icon

* Adjust livestream icon

* Save scheduled livestreasm & tile ad

* Fix scheduled callback

* Fix playlist icon size on file page

* Fix grid distortion in 3 & 4 column layout

* -

* Fix grid on category & channel page

* Fix Premium Plus Grid

* Add custom tile for adblockers

* Reset env

* Remove collapsed tiles

* Remove setLoaded on scheduled livestreams page

* -

* Make isHidden optional

* Remove px

* Review adjustments

* Inject Premium+ ads

* Fix injection

* Fix injection when using the last tile

* Fix injection when using the last tile

* Enable stripe dev

* Create PremiumPlusTile component and add list view design

* Create PremiumPlusTile component and add list view design

* Adjust ads in list view

* Remove setState from render loop

* Clean code

* Fix livestream margin in list view

* Rewrite & tune some logic - Homepage & Channel page

* Clean details...

* Clean details...

* Requested review changes

Signed-off-by: Raphael Wickihalder <raphael.wickihalder@odysee.com>

* Requested review changes

Signed-off-by: Raphael Wickihalder <raphael.wickihalder@odysee.com>
2022-05-18 13:16:35 +02:00

311 lines
10 KiB
JavaScript

// @flow
import React from 'react';
import classnames from 'classnames';
import { NavLink, withRouter } from 'react-router-dom';
import ClaimPreviewProgress from 'component/claimPreviewProgress';
import FileThumbnail from 'component/fileThumbnail';
import UriIndicator from 'component/uriIndicator';
import Icon from 'component/common/icon';
import TruncatedText from 'component/common/truncated-text';
import DateTime from 'component/dateTime';
import LivestreamDateTime from 'component/livestreamDateTime';
import ChannelThumbnail from 'component/channelThumbnail';
import FileViewCountInline from 'component/fileViewCountInline';
// import SubscribeButton from 'component/subscribeButton';
import useGetThumbnail from 'effects/use-get-thumbnail';
import { formatLbryUrlForWeb, generateListSearchUrlParams } from 'util/url';
import { formatClaimPreviewTitle } from 'util/formatAriaLabel';
import { parseURI } from 'util/lbryURI';
import PreviewOverlayProperties from 'component/previewOverlayProperties';
import FileDownloadLink from 'component/fileDownloadLink';
import FileHideRecommendation from 'component/fileHideRecommendation';
import FileWatchLaterLink from 'component/fileWatchLaterLink';
import ClaimRepostAuthor from 'component/claimRepostAuthor';
import ClaimMenuList from 'component/claimMenuList';
import CollectionPreviewOverlay from 'component/collectionPreviewOverlay';
import * as ICONS from 'constants/icons';
import { FYP_ID } from 'constants/urlParams';
// $FlowFixMe cannot resolve ...
import PlaceholderTx from 'static/img/placeholderTx.gif';
type Props = {
uri: string,
date?: any,
claim: ?Claim,
mediaDuration?: string,
resolveUri: (string) => void,
isResolvingUri: boolean,
history: { push: (string) => void },
thumbnail: string,
title: string,
placeholder: boolean,
banState: { blacklisted?: boolean, filtered?: boolean, muted?: boolean, blocked?: boolean },
getFile: (string) => void,
streamingUrl: string,
isMature: boolean,
showMature: boolean,
showHiddenByUser?: boolean,
showNoSourceClaims?: boolean,
showUnresolvedClaims?: boolean,
properties?: (Claim) => void,
collectionId?: string,
fypId?: string,
isLivestream: boolean,
viewCount: string,
isLivestreamActive: boolean,
livestreamViewerCount: ?number,
swipeLayout: boolean,
onHidden?: (string) => void,
pulse?: boolean,
};
// preview image cards used in related video functionality, channel overview page and homepage
function ClaimPreviewTile(props: Props) {
const {
history,
uri,
date,
isResolvingUri,
thumbnail,
title,
resolveUri,
claim,
placeholder,
banState,
getFile,
streamingUrl,
isMature,
showMature,
showHiddenByUser,
properties,
showNoSourceClaims,
showUnresolvedClaims,
isLivestream,
isLivestreamActive,
livestreamViewerCount,
collectionId,
fypId,
mediaDuration,
viewCount,
swipeLayout = false,
onHidden,
pulse,
} = props;
const isRepost = claim && claim.repost_channel_url;
const isCollection = claim && claim.value_type === 'collection';
const isStream = claim && claim.value_type === 'stream';
const isAbandoned = !isResolvingUri && !claim;
// $FlowFixMe
const isPlayable =
claim &&
// $FlowFixMe
claim.value &&
// $FlowFixMe
claim.value.stream_type &&
// $FlowFixMe
(claim.value.stream_type === 'audio' || claim.value.stream_type === 'video');
const collectionClaimId = isCollection && claim && claim.claim_id;
const shouldFetch = claim === undefined;
const thumbnailUrl = useGetThumbnail(uri, claim, streamingUrl, getFile, placeholder) || thumbnail;
const canonicalUrl = claim && claim.canonical_url;
const repostedContentUri = claim && (claim.reposted_claim ? claim.reposted_claim.permanent_url : claim.permanent_url);
const listId = collectionId || collectionClaimId;
const navigateUrl =
formatLbryUrlForWeb(canonicalUrl || uri || '/') +
(listId ? generateListSearchUrlParams(listId) : '') +
(fypId ? `?${FYP_ID}=${fypId}` : ''); // sigh...
const navLinkProps = {
to: navigateUrl,
onClick: (e) => e.stopPropagation(),
};
let isValid = false;
if (uri) {
try {
parseURI(uri);
isValid = true;
} catch (e) {
isValid = false;
}
}
const signingChannel = claim && claim.signing_channel;
const isChannel = claim && claim.value_type === 'channel';
const channelUri = !isChannel ? signingChannel && signingChannel.permanent_url : claim && claim.permanent_url;
const channelTitle = signingChannel && ((signingChannel.value && signingChannel.value.title) || signingChannel.name);
// Aria-label value for claim preview
let ariaLabelData = isChannel ? title : formatClaimPreviewTitle(title, channelTitle, date, mediaDuration);
function handleClick(e) {
if (navigateUrl) {
history.push(navigateUrl);
}
}
React.useEffect(() => {
if (isValid && !isResolvingUri && shouldFetch && uri) {
resolveUri(uri);
}
}, [isValid, isResolvingUri, uri, resolveUri, shouldFetch]);
let shouldHide = false;
if (isMature && !showMature) {
// Unfortunately needed until this is resolved
// https://github.com/lbryio/lbry-sdk/issues/2785
shouldHide = true;
} else {
shouldHide =
!placeholder &&
(banState.blacklisted ||
banState.filtered ||
(!showHiddenByUser && (banState.muted || banState.blocked)) ||
(isAbandoned && !showUnresolvedClaims));
if (onHidden && shouldHide) onHidden(props.uri);
}
if (shouldHide || (isLivestream && !showNoSourceClaims)) {
return null;
}
const isChannelPage = window.location.pathname.startsWith('/@');
const shouldShowViewCount = !(!viewCount || (claim && claim.repost_url) || isLivestream || !isChannelPage);
if (placeholder || (!claim && isResolvingUri)) {
return (
<li
className={classnames('placeholder claim-preview--tile', {
'swipe-list__item claim-preview--horizontal-tile': swipeLayout,
pulse: pulse,
})}
>
<div className="media__thumb">
<img src={PlaceholderTx} alt="Placeholder" />
</div>
<div className="placeholder__wrapper">
<div className="claim-tile__title" />
<div className="claim-tile__title_b" />
<div
className={classnames('claim-tile__info', {
contains_view_count: shouldShowViewCount,
})}
>
<div className="channel-thumbnail" />
<div className="claim-tile__about">
<div className="button__content" />
<div className="claim-tile__about--counts" />
</div>
</div>
</div>
</li>
);
}
let liveProperty = null;
if (isLivestream) {
if (isLivestreamActive === true && livestreamViewerCount) {
liveProperty = (claim) => (
<span className="livestream__viewer-count">
{livestreamViewerCount} <Icon icon={ICONS.EYE} />
</span>
);
} else {
liveProperty = (claim) => <>LIVE</>;
}
}
return (
<li
onClick={handleClick}
className={classnames('card claim-preview--tile', {
'claim-preview__wrapper--channel': isChannel,
'claim-preview__live': isLivestreamActive,
'swipe-list__item claim-preview--horizontal-tile': swipeLayout,
})}
>
<NavLink {...navLinkProps} role="none" tabIndex={-1} aria-hidden>
<FileThumbnail thumbnail={thumbnailUrl} allowGifs>
{!isChannel && (
<React.Fragment>
<div className="claim-preview__hover-actions">
{isPlayable && <FileWatchLaterLink focusable={false} uri={repostedContentUri} />}
</div>
{fypId && (
<div className="claim-preview__hover-actions">
{isStream && <FileHideRecommendation focusable={false} uri={repostedContentUri} />}
</div>
)}
{/* @if TARGET='app' */}
<div className="claim-preview__hover-actions">
{isStream && <FileDownloadLink focusable={false} uri={canonicalUrl} hideOpenButton />}
</div>
{/* @endif */}
<div className="claim-preview__file-property-overlay">
<PreviewOverlayProperties uri={uri} properties={liveProperty || properties} />
</div>
<ClaimPreviewProgress uri={uri} />
</React.Fragment>
)}
{isCollection && (
<React.Fragment>
<div className="claim-preview__collection-wrapper">
<CollectionPreviewOverlay collectionId={listId} uri={uri} />
</div>
</React.Fragment>
)}
</FileThumbnail>
</NavLink>
<div className="claim-tile__header">
<NavLink aria-label={ariaLabelData} {...navLinkProps}>
<h2 className="claim-tile__title">
<TruncatedText text={title || (claim && claim.name)} lines={isChannel ? 1 : 2} />
{isChannel && (
<div className="claim-tile__about">
<UriIndicator uri={uri} />
</div>
)}
</h2>
</NavLink>
<ClaimMenuList uri={uri} collectionId={listId} fypId={fypId} channelUri={channelUri} />
</div>
<div>
<div
className={classnames('claim-tile__info', {
contains_view_count: shouldShowViewCount,
})}
>
{isChannel ? (
// <div className="claim-tile__about--channel">
// <SubscribeButton uri={repostedChannelUri || uri} />
// </div>
<></>
) : (
<React.Fragment>
<UriIndicator focusable={false} uri={uri} link hideAnonymous>
<ChannelThumbnail uri={channelUri} xsmall checkMembership={false} />
</UriIndicator>
<div className="claim-tile__about">
<UriIndicator uri={uri} link />
<div className="claim-tile__about--counts">
<FileViewCountInline uri={uri} isLivestream={isLivestream} />
{isLivestream && <LivestreamDateTime uri={uri} />}
{!isLivestream && <DateTime timeAgo uri={uri} />}
</div>
</div>
</React.Fragment>
)}
</div>
{isRepost && (
<div className="claim-tile__repost-author">
<ClaimRepostAuthor uri={uri} />
</div>
)}
</div>
</li>
);
}
export default withRouter(ClaimPreviewTile);