basic display of reposts in app

This commit is contained in:
Sean Yesmunt 2020-01-31 11:43:14 -05:00
parent e94bf5d33e
commit 77e26eb440
10 changed files with 192 additions and 90 deletions

View file

@ -130,7 +130,7 @@
"imagesloaded": "^4.1.4", "imagesloaded": "^4.1.4",
"json-loader": "^0.5.4", "json-loader": "^0.5.4",
"lbry-format": "https://github.com/lbryio/lbry-format.git", "lbry-format": "https://github.com/lbryio/lbry-format.git",
"lbry-redux": "lbryio/lbry-redux#71e85536dbe397df98a47a3c4b4311760b2e7419", "lbry-redux": "lbryio/lbry-redux#bfbaa0dbdd2c1b2b340c0760d0d97c99f3cefb02",
"lbryinc": "lbryio/lbryinc#6a59102c52673502569d2c43bd4ee58c315fb2e4", "lbryinc": "lbryio/lbryinc#6a59102c52673502569d2c43bd4ee58c315fb2e4",
"lint-staged": "^7.0.2", "lint-staged": "^7.0.2",
"localforage": "^1.7.1", "localforage": "^1.7.1",

View file

@ -17,6 +17,7 @@ import BlockButton from 'component/blockButton';
import useGetThumbnail from 'effects/use-get-thumbnail'; import useGetThumbnail from 'effects/use-get-thumbnail';
import ClaimPreviewTitle from 'component/claimPreviewTitle'; import ClaimPreviewTitle from 'component/claimPreviewTitle';
import ClaimPreviewSubtitle from 'component/claimPreviewSubtitle'; import ClaimPreviewSubtitle from 'component/claimPreviewSubtitle';
import ClaimRepostAuthor from 'component/claimRepostAuthor';
type Props = { type Props = {
uri: string, uri: string,
@ -177,11 +178,19 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
if (placeholder === 'loading' || (isResolvingUri && !claim)) { if (placeholder === 'loading' || (isResolvingUri && !claim)) {
return ( return (
<li className={classnames('claim-preview', { 'claim-preview--large': type === 'large' })} disabled> <li
<div className="placeholder media__thumb" /> disabled
<div className="placeholder__wrapper"> className={classnames('claim-preview__wrapper', {
<div className="placeholder claim-preview__title" /> 'claim-preview__wrapper--channel': isChannel && type !== 'inline',
<div className="placeholder media__subtitle" /> 'claim-preview__wrapper--inline': type === 'inline',
})}
>
<div className={classnames('claim-preview', { 'claim-preview--large': type === 'large' })}>
<div className="placeholder media__thumb" />
<div className="placeholder__wrapper">
<div className="placeholder claim-preview__title" />
<div className="placeholder media__subtitle" />
</div>
</div> </div>
</li> </li>
); );
@ -197,50 +206,61 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
role="link" role="link"
onClick={pending || type === 'inline' ? undefined : handleOnClick} onClick={pending || type === 'inline' ? undefined : handleOnClick}
onContextMenu={handleContextMenu} onContextMenu={handleContextMenu}
className={classnames('claim-preview', { className={classnames('claim-preview__wrapper', {
'claim-preview--small': type === 'small' || type === 'tooltip', 'claim-preview__wrapper--channel': isChannel && type !== 'inline',
'claim-preview--large': type === 'large', 'claim-preview__wrapper--inline': type === 'inline',
'claim-preview--inline': type === 'inline', 'claim-preview__wrapper--small': type === 'small',
'claim-preview--tooltip': type === 'tooltip',
'claim-preview--channel': isChannel,
'claim-preview--visited': !isChannel && !claimIsMine && hasVisitedUri,
'claim-preview--pending': pending,
})} })}
> >
{isChannel && claim ? ( {type !== 'large' && type !== 'inline' && <ClaimRepostAuthor uri={uri} />}
<UriIndicator uri={uri} link>
<ChannelThumbnail uri={uri} obscure={channelIsBlocked} />
</UriIndicator>
) : (
<FileThumbnail thumbnail={thumbnailUrl} />
)}
<div className="claim-preview__text">
<div className="claim-preview-metadata">
<div className="claim-preview-info">
<ClaimPreviewTitle uri={uri} />
{!isChannel && <FileProperties uri={uri} />}
</div>
<ClaimPreviewSubtitle uri={uri} type={type} /> <div
</div> className={classnames('claim-preview', {
<div className="claim-preview__actions"> 'claim-preview--small': type === 'small' || type === 'tooltip',
{!pending && ( 'claim-preview--large': type === 'large',
<React.Fragment> 'claim-preview--inline': type === 'inline',
{hideActions ? null : actions !== undefined ? ( 'claim-preview--tooltip': type === 'tooltip',
actions 'claim-preview--channel': isChannel,
) : ( 'claim-preview--visited': !isChannel && !claimIsMine && hasVisitedUri,
<div className="card__actions--inline"> 'claim-preview--pending': pending,
{isChannel && !channelIsBlocked && !claimIsMine && ( })}
<SubscribeButton uri={uri.startsWith('lbry://') ? uri : `lbry://${uri}`} /> >
)} {isChannel && claim ? (
{!hideBlock && isChannel && !isSubscribed && !claimIsMine && ( <UriIndicator uri={uri} link>
<BlockButton uri={uri.startsWith('lbry://') ? uri : `lbry://${uri}`} /> <ChannelThumbnail uri={uri} obscure={channelIsBlocked} />
)} </UriIndicator>
</div> ) : (
)} <FileThumbnail thumbnail={thumbnailUrl} />
</React.Fragment> )}
)}
{properties !== undefined ? properties : <ClaimTags uri={uri} type={type} />} <div className="claim-preview__text">
<div className="claim-preview-metadata">
<div className="claim-preview-info">
<ClaimPreviewTitle uri={uri} />
{!isChannel && <FileProperties uri={uri} />}
</div>
<ClaimPreviewSubtitle uri={uri} type={type} />
</div>
<div className="claim-preview__actions">
{!pending && (
<React.Fragment>
{hideActions ? null : actions !== undefined ? (
actions
) : (
<div className="card__actions--inline">
{isChannel && !channelIsBlocked && !claimIsMine && (
<SubscribeButton uri={uri.startsWith('lbry://') ? uri : `lbry://${uri}`} />
)}
{!hideBlock && isChannel && !isSubscribed && !claimIsMine && (
<BlockButton uri={uri.startsWith('lbry://') ? uri : `lbry://${uri}`} />
)}
</div>
)}
</React.Fragment>
)}
{properties !== undefined ? properties : <ClaimTags uri={uri} type={type} />}
</div>
</div> </div>
</div> </div>
</li> </li>

View file

@ -12,8 +12,8 @@ import useGetThumbnail from 'effects/use-get-thumbnail';
import { formatLbryUrlForWeb } from 'util/url'; import { formatLbryUrlForWeb } from 'util/url';
import { parseURI } from 'lbry-redux'; import { parseURI } from 'lbry-redux';
import FileProperties from 'component/fileProperties'; import FileProperties from 'component/fileProperties';
import FileDownloadLink from 'component/fileDownloadLink'; import FileDownloadLink from 'component/fileDownloadLink';
import ClaimRepostAuthor from 'component/claimRepostAuthor';
type Props = { type Props = {
uri: string, uri: string,
@ -54,6 +54,7 @@ function ClaimPreviewTile(props: Props) {
streamingUrl, streamingUrl,
blockedChannelUris, blockedChannelUris,
} = props; } = props;
const isRepost = claim && claim.repost_channel_url;
const shouldFetch = claim === undefined; const shouldFetch = claim === undefined;
const thumbnailUrl = useGetThumbnail(uri, claim, streamingUrl, getFile, placeholder) || thumbnail; const thumbnailUrl = useGetThumbnail(uri, claim, streamingUrl, getFile, placeholder) || thumbnail;
const claimsInChannel = (claim && claim.meta.claims_in_channel) || 0; const claimsInChannel = (claim && claim.meta.claims_in_channel) || 0;
@ -146,7 +147,7 @@ function ClaimPreviewTile(props: Props) {
role="link" role="link"
onClick={handleClick} onClick={handleClick}
className={classnames('card claim-preview--tile', { className={classnames('card claim-preview--tile', {
'claim-preview--channel': isChannel, 'claim-preview__wrapper--channel': isChannel,
})} })}
> >
<NavLink {...navLinkProps}> <NavLink {...navLinkProps}>
@ -170,27 +171,34 @@ function ClaimPreviewTile(props: Props) {
<TruncatedText text={title || (claim && claim.name)} lines={2} /> <TruncatedText text={title || (claim && claim.name)} lines={2} />
</h2> </h2>
</NavLink> </NavLink>
<div className="claim-tile__info"> <div>
{isChannel ? ( <div className="claim-tile__info">
<div className="claim-tile__about--channel"> {isChannel ? (
<SubscribeButton uri={uri} /> <div className="claim-tile__about--channel">
<span className="claim-tile__publishes"> <SubscribeButton uri={uri} />
{claimsInChannel === 1 <span className="claim-tile__publishes">
? __('%claimsInChannel% publish', { claimsInChannel }) {claimsInChannel === 1
: __('%claimsInChannel% publishes', { claimsInChannel })} ? __('%claimsInChannel% publish', { claimsInChannel })
</span> : __('%claimsInChannel% publishes', { claimsInChannel })}
</div> </span>
) : (
<React.Fragment>
<UriIndicator uri={uri} link hideAnonymous>
<ChannelThumbnail thumbnailPreview={channelThumbnail} />
</UriIndicator>
<div className="claim-tile__about">
<UriIndicator uri={uri} link />
<DateTime timeAgo uri={uri} />
</div> </div>
</React.Fragment> ) : (
<React.Fragment>
<UriIndicator uri={uri} link hideAnonymous>
<ChannelThumbnail thumbnailPreview={channelThumbnail} />
</UriIndicator>
<div className="claim-tile__about">
<UriIndicator uri={uri} link />
<DateTime timeAgo uri={uri} />
</div>
</React.Fragment>
)}
</div>
{isRepost && (
<div className="claim-tile__repost-author">
<ClaimRepostAuthor uri={uri} />
</div>
)} )}
</div> </div>
</li> </li>

View file

@ -0,0 +1,9 @@
import { connect } from 'react-redux';
import { makeSelectClaimForUri } from 'lbry-redux';
import ClaimRepostAuthor from './view';
const select = (state, props) => ({
claim: makeSelectClaimForUri(props.uri)(state),
});
export default connect(select)(ClaimRepostAuthor);

View file

@ -0,0 +1,31 @@
// @flow
import * as ICONS from 'constants/icons';
import React from 'react';
import I18nMessage from 'component/i18nMessage';
import UriIndicator from 'component/uriIndicator';
import Icon from 'component/common/icon';
type Props = {
uri: string,
claim: ?Claim,
};
function ClaimRepostAuthor(props: Props) {
const { claim } = props;
const repostChannelUrl = claim && claim.repost_channel_url;
if (!repostChannelUrl) {
return null;
}
return (
<div className="claim-preview__repost-author">
<Icon icon={ICONS.REPOST} size={10} />
<I18nMessage tokens={{ repost_channel_link: <UriIndicator link uri={repostChannelUrl} /> }}>
%repost_channel_link% reposted
</I18nMessage>
</div>
);
}
export default ClaimRepostAuthor;

View file

@ -392,4 +392,12 @@ export const icons = {
<polyline points="7 3 7 8 15 8" /> <polyline points="7 3 7 8 15 8" />
</g> </g>
), ),
[ICONS.REPOST]: buildIcon(
<g>
<polyline points="17 1 21 5 17 9" />
<path d="M3 11V9a4 4 0 0 1 4-4h14" />
<polyline points="7 23 3 19 7 15" />
<path d="M21 13v2a4 4 0 0 1-4 4H3" />
</g>
),
}; };

View file

@ -86,3 +86,4 @@ export const AUDIO = 'HeadPhones';
export const VIDEO = 'Video'; export const VIDEO = 'Video';
export const TEXT = 'FileText'; export const TEXT = 'FileText';
export const DOWNLOADABLE = 'Downloadable'; export const DOWNLOADABLE = 'Downloadable';
export const REPOST = 'Repeat';

View file

@ -1,5 +1,5 @@
.claim-list { .claim-list {
.claim-preview { .claim-preview__wrapper {
border-bottom: 1px solid var(--color-border); border-bottom: 1px solid var(--color-border);
} }
} }
@ -60,12 +60,28 @@
} }
} }
.claim-preview__wrapper {
padding: var(--spacing-medium);
list-style: none;
}
.claim-preview__wrapper--channel {
background-color: var(--color-card-background-highlighted);
}
.claim-preview__wrapper--inline {
padding: 0;
}
.claim-preview__wrapper--small {
padding: var(--spacing-small);
}
.claim-preview { .claim-preview {
flex: 1; flex: 1;
display: flex; display: flex;
position: relative; position: relative;
overflow: visible; overflow: visible;
padding: var(--spacing-medium);
&:not(.claim-preview--inline):not(.claim-preview--pending):not(.claim-preview--inactive) { &:not(.claim-preview--inline):not(.claim-preview--pending):not(.claim-preview--inactive) {
cursor: pointer; cursor: pointer;
@ -112,8 +128,6 @@
} }
.claim-preview--small { .claim-preview--small {
padding: var(--spacing-small);
.media__thumb { .media__thumb {
width: 8rem; width: 8rem;
} }
@ -124,18 +138,11 @@
} }
} }
.claim-preview--channel:not(.claim-preview--inline) {
background-color: var(--color-card-background-highlighted);
}
.claim-preview--pending { .claim-preview--pending {
opacity: 0.6; opacity: 0.6;
} }
.claim-preview--inline { .claim-preview--inline {
padding: 0;
border-bottom: none;
.channel-thumbnail { .channel-thumbnail {
width: var(--channel-thumbnail-width--small); width: var(--channel-thumbnail-width--small);
height: var(--channel-thumbnail-width--small); height: var(--channel-thumbnail-width--small);
@ -413,3 +420,20 @@
} }
} }
} }
.claim-preview__repost-author {
transform: translateY(calc(var(--spacing-small) * -1));
font-size: var(--font-xsmall);
color: var(--color-text-subtitle);
line-height: 1;
.icon {
margin-right: var(--spacing-miniscule);
margin-bottom: -1px; // Offset it slightly because it doesn't look aligned next to all lowercase text + the @ from a channel
}
}
.claim-tile__repost-author {
margin: var(--spacing-small);
margin-bottom: 0;
}

View file

@ -28,6 +28,7 @@
.file-properties--large { .file-properties--large {
flex-wrap: wrap; flex-wrap: wrap;
margin-bottom: var(--spacing-large); margin-bottom: var(--spacing-large);
margin-left: 0;
& > * { & > * {
margin-top: var(--spacing-small); margin-top: var(--spacing-small);

View file

@ -1073,10 +1073,10 @@
resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.0.tgz#eef87a431300f6148c39a7f75f8cfeb218b2547e" resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.0.tgz#eef87a431300f6148c39a7f75f8cfeb218b2547e"
integrity sha512-ywoxP68aOT3zHCLgWZgwUJatiENeHE7xJzYjfz8WI0goynp96wETBF+d95b8g/uL4QmS6owPVlaxiz3wyMAzcw== integrity sha512-ywoxP68aOT3zHCLgWZgwUJatiENeHE7xJzYjfz8WI0goynp96wETBF+d95b8g/uL4QmS6owPVlaxiz3wyMAzcw==
"@octokit/plugin-rest-endpoint-methods@2.1.3": "@octokit/plugin-rest-endpoint-methods@^2.1.0":
version "2.1.3" version "2.1.2"
resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-2.1.3.tgz#a316af20ac9465af3fcbd0da3e4b764d94703ed2" resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-2.1.2.tgz#c9b72657a1dca049aa8f51cca7d12faa85ccefc1"
integrity sha512-YtP757n6UIyVhd4YJ0aerx7CuO4+VJJ5BYlvD2aPnUPhCoNbINSxVS1p7UEBRzMOrPfEGDYEcs/tjADB1NOCyg== integrity sha512-PS77CqifhDqYONWAxLh+BKGlmuhdEX39JVEVQoWWDvkh5B+2bcg9eaxMEFUEJtfuqdAw33sdGrrlGtqtl+9lqg==
dependencies: dependencies:
"@octokit/types" "^2.0.1" "@octokit/types" "^2.0.1"
deprecation "^2.3.1" deprecation "^2.3.1"
@ -1120,14 +1120,14 @@
url-template "^2.0.8" url-template "^2.0.8"
"@octokit/rest@^16.40.0": "@octokit/rest@^16.40.0":
version "16.41.0" version "16.40.2"
resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-16.41.0.tgz#28950d7bb854da3bf36bd317c95526ffbcb7b982" resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-16.40.2.tgz#f32ecb96d4cfadd3ab8fbfeb91a6018368d03956"
integrity sha512-qdXSdoI7RI+u04KR5FEv2UAo8kuCLH1l48mLCQu3gpMoCapmIXL0dGDjzhAbuQHRxkGz2zuvCKhskdMhy66Nww== integrity sha512-6JYp+VQOPGYBPbpVA+rat0VWMLACjbWz4iwpbpcTeXQj8SlxZRErUcRk7hvUNK5swyf2GHAwKG6Kpl3xdYxVrA==
dependencies: dependencies:
"@octokit/auth-token" "^2.4.0" "@octokit/auth-token" "^2.4.0"
"@octokit/plugin-paginate-rest" "^1.1.1" "@octokit/plugin-paginate-rest" "^1.1.1"
"@octokit/plugin-request-log" "^1.0.0" "@octokit/plugin-request-log" "^1.0.0"
"@octokit/plugin-rest-endpoint-methods" "2.1.3" "@octokit/plugin-rest-endpoint-methods" "^2.1.0"
"@octokit/request" "^5.2.0" "@octokit/request" "^5.2.0"
"@octokit/request-error" "^1.0.2" "@octokit/request-error" "^1.0.2"
atob-lite "^2.0.0" atob-lite "^2.0.0"
@ -7277,9 +7277,9 @@ lazy-val@^1.0.4:
yargs "^13.2.2" yargs "^13.2.2"
zstd-codec "^0.1.1" zstd-codec "^0.1.1"
lbry-redux@lbryio/lbry-redux#71e85536dbe397df98a47a3c4b4311760b2e7419: lbry-redux@lbryio/lbry-redux#bfbaa0dbdd2c1b2b340c0760d0d97c99f3cefb02:
version "0.0.1" version "0.0.1"
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/71e85536dbe397df98a47a3c4b4311760b2e7419" resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/bfbaa0dbdd2c1b2b340c0760d0d97c99f3cefb02"
dependencies: dependencies:
proxy-polyfill "0.1.6" proxy-polyfill "0.1.6"
reselect "^3.0.0" reselect "^3.0.0"