better posts styling

This commit is contained in:
Sean Yesmunt 2021-03-11 12:08:11 -05:00
parent 4e9a4f41c8
commit 7702477e71
23 changed files with 342 additions and 159 deletions

View file

@ -254,7 +254,6 @@ export default function GetHomePageRowData(
rowData.push(YOUTUBE_CREATOR_ROW);
}
// rowData.push(TRENDING_CLASSICS);
rowData.push(TOP_CONTENT_TODAY);
// rowData.push(TRENDING_ON_LBRY);

View file

@ -899,7 +899,6 @@
"Already have an account? %sign_in%": "Already have an account? %sign_in%",
"Sign in with a password (optional)": "Sign in with a password (optional)",
"Don't have an account? %sign_up%": "Don't have an account? %sign_up%",
"Preparing your content": "Preparing your content",
"File details": "File details",
"You can unlock all or some of these LBRY Credits at any time.": "You can unlock all or some of these LBRY Credits at any time.",
"Keeping it locked improves the trust and discoverability of your content.": "Keeping it locked improves the trust and discoverability of your content.",

View file

@ -53,7 +53,7 @@ export default function ClaimTags(props: Props) {
return (
<div className={classnames('file-properties--small', { 'file-properties--large': type === 'large' })}>
{tagsToDisplay.map(tag => (
{tagsToDisplay.map((tag) => (
<Tag key={tag} title={tag} name={tag} />
))}
</div>

View file

@ -4,13 +4,14 @@ import ClaimPreview from 'component/claimPreview';
type Props = {
channelUri: string,
hideActions?: boolean,
};
function FileAuthor(props: Props) {
const { channelUri } = props;
const { channelUri, hideActions } = props;
return channelUri ? (
<ClaimPreview uri={channelUri} type="inline" properties={false} hideMenu />
<ClaimPreview uri={channelUri} type="inline" properties={false} hideMenu hideActions={hideActions} />
) : (
<div className="claim-preview--inline claim-preview__title">{__('Anonymous')}</div>
);

View file

@ -14,12 +14,12 @@ import Nag from 'component/common/nag';
const SPACE_BAR_KEYCODE = 32;
type Props = {
play: string => void,
play: (string) => void,
isLoading: boolean,
isPlaying: boolean,
fileInfo: FileListItem,
uri: string,
history: { push: string => void },
history: { push: (string) => void },
location: { search: ?string, pathname: string },
obscurePreview: boolean,
insufficientCredits: boolean,
@ -58,6 +58,7 @@ export default function FileRenderInitiator(props: Props) {
const isFree = hasCostInfo && cost === 0;
const fileStatus = fileInfo && fileInfo.status;
const isPlayable = RENDER_MODES.FLOATING_MODES.includes(renderMode);
const isText = RENDER_MODES.TEXT_MODES.includes(renderMode);
function doAuthRedirect() {
history.push(`/$/${PAGES.AUTH}?redirect=${encodeURIComponent(location.pathname)}`);
@ -121,6 +122,7 @@ export default function FileRenderInitiator(props: Props) {
className={classnames('content__cover', {
'content__cover--disabled': disabled,
'content__cover--theater-mode': videoTheaterMode,
'content__cover--text': isText,
'card__media--nsfw': obscurePreview,
})}
>

View file

@ -52,5 +52,5 @@ export default function FileRenderInline(props: Props) {
return null;
}
return renderContent ? <FileRender uri={uri} /> : <LoadingScreen status={__('Preparing your content')} isDocument />;
return renderContent ? <FileRender uri={uri} /> : <LoadingScreen isDocument />;
}

View file

@ -1,11 +1,9 @@
import { connect } from 'react-redux';
import { makeSelectTitleForUri } from 'lbry-redux';
import { makeSelectInsufficientCreditsForUri } from 'redux/selectors/content';
import FileTitle from './view';
import FileTitleSection from './view';
const select = (state, props) => ({
isInsufficientCredits: makeSelectInsufficientCreditsForUri(props.uri)(state),
title: makeSelectTitleForUri(props.uri)(state),
});
export default connect(select)(FileTitle);
export default connect(select)(FileTitleSection);

View file

@ -1,76 +1,22 @@
// @flow
import type { Node } from 'react';
import * as React from 'react';
import { normalizeURI } from 'lbry-redux';
import FilePrice from 'component/filePrice';
import ClaimInsufficientCredits from 'component/claimInsufficientCredits';
import FileSubtitle from 'component/fileSubtitle';
import FileAuthor from 'component/fileAuthor';
import Card from 'component/common/card';
import * as ICONS from 'constants/icons';
import Icon from 'component/common/icon';
import I18nMessage from 'component/i18nMessage';
import Button from 'component/button';
import * as PAGES from 'constants/pages';
import FileDescription from 'component/fileDescription';
import classnames from 'classnames';
type Props = {
uri: string,
title: string,
nsfw: boolean,
isNsfwBlocked: boolean,
className?: string,
children?: Node,
};
function FileTitle(props: Props) {
const { title, uri, nsfw, isNsfwBlocked } = props;
const { title, children, className } = props;
return (
<Card
isPageTitle
noTitleWrap
title={
<React.Fragment>
{title}
{nsfw && (
<span className="media__title-badge">
<span className="badge badge--tag-mature">{__('Mature')}</span>
</span>
)}
</React.Fragment>
}
titleActions={<FilePrice uri={normalizeURI(uri)} type="filepage" />}
body={
<React.Fragment>
<ClaimInsufficientCredits uri={uri} />
<FileSubtitle uri={uri} />
</React.Fragment>
}
actions={
isNsfwBlocked ? (
<div className="main--empty">
<h2>
<Icon className="icon--hidden" icon={ICONS.EYE_OFF} />
{__('Mature content blocked.')}
</h2>
<div>
<I18nMessage
tokens={{
content_settings: (
<Button button="link" label={__('content settings')} navigate={`/$/${PAGES.SETTINGS}`} />
),
}}
>
Change this in your %content_settings%.
</I18nMessage>
</div>
</div>
) : (
<div className="section">
<FileAuthor uri={uri} />
<FileDescription uri={uri} />
</div>
)
}
/>
<h1 className={classnames(className)}>
<span>{title}</span>
{children}
</h1>
);
}

View file

@ -0,0 +1,11 @@
import { connect } from 'react-redux';
import { makeSelectTitleForUri } from 'lbry-redux';
import { makeSelectInsufficientCreditsForUri } from 'redux/selectors/content';
import FileTitleSection from './view';
const select = (state, props) => ({
isInsufficientCredits: makeSelectInsufficientCreditsForUri(props.uri)(state),
title: makeSelectTitleForUri(props.uri)(state),
});
export default connect(select)(FileTitleSection);

View file

@ -0,0 +1,77 @@
// @flow
import * as React from 'react';
import { normalizeURI } from 'lbry-redux';
import FilePrice from 'component/filePrice';
import ClaimInsufficientCredits from 'component/claimInsufficientCredits';
import FileSubtitle from 'component/fileSubtitle';
import FileAuthor from 'component/fileAuthor';
import Card from 'component/common/card';
import * as ICONS from 'constants/icons';
import Icon from 'component/common/icon';
import I18nMessage from 'component/i18nMessage';
import Button from 'component/button';
import * as PAGES from 'constants/pages';
import FileDescription from 'component/fileDescription';
type Props = {
uri: string,
title: string,
nsfw: boolean,
isNsfwBlocked: boolean,
};
function FileTitleSection(props: Props) {
const { title, uri, nsfw, isNsfwBlocked } = props;
return (
<Card
isPageTitle
noTitleWrap
title={
<React.Fragment>
{title}
{nsfw && (
<span className="media__title-badge">
<span className="badge badge--tag-mature">{__('Mature')}</span>
</span>
)}
</React.Fragment>
}
titleActions={<FilePrice uri={normalizeURI(uri)} type="filepage" />}
body={
<React.Fragment>
<ClaimInsufficientCredits uri={uri} />
<FileSubtitle uri={uri} />
</React.Fragment>
}
actions={
isNsfwBlocked ? (
<div className="main--empty">
<h2>
<Icon className="icon--hidden" icon={ICONS.EYE_OFF} />
{__('Mature content blocked.')}
</h2>
<div>
<I18nMessage
tokens={{
content_settings: (
<Button button="link" label={__('content settings')} navigate={`/$/${PAGES.SETTINGS}`} />
),
}}
>
Change this in your %content_settings%.
</I18nMessage>
</div>
</div>
) : (
<div className="section">
<FileAuthor uri={uri} />
<FileDescription uri={uri} />
</div>
)
}
/>
);
}
export default FileTitleSection;

View file

@ -27,6 +27,7 @@ type Props = {
noSideNavigation: boolean,
fullWidthPage: boolean,
videoTheaterMode: boolean,
isText?: boolean,
backout: {
backLabel?: string,
backNavDefault?: string,
@ -47,6 +48,7 @@ function Page(props: Props) {
noSideNavigation = false,
backout,
videoTheaterMode,
isText = false,
} = props;
const {
@ -105,6 +107,7 @@ function Page(props: Props) {
'main--auth-page': authPage,
'main--file-page': filePage,
'main--theater-mode': isOnFilePage && videoTheaterMode,
'main--text': isText,
})}
>
{children}

View file

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

View file

@ -0,0 +1,49 @@
// @flow
import * as React from 'react';
import FileAuthor from 'component/fileAuthor';
import FileTitle from 'component/fileTitle';
import FileActions from 'component/fileActions';
import FileRenderInitiator from 'component/fileRenderInitiator';
import FileRenderInline from 'component/fileRenderInline';
import FileViewCount from 'component/fileViewCount';
import { formatCredits } from 'lbry-redux';
import CreditAmount from 'component/common/credit-amount';
import DateTime from 'component/dateTime';
type Props = {
uri: string,
claim: ?StreamClaim,
};
function PostViewer(props: Props) {
const { uri, claim } = props;
if (!claim) {
return null;
}
const amount = parseFloat(claim.amount) + parseFloat(claim.meta.support_amount);
const formattedAmount = formatCredits(amount, 2, true);
return (
<div className="post">
<FileTitle uri={uri} className="post__title">
<span className="post__date">
<DateTime uri={uri} show={DateTime.SHOW_DATE} />
</span>
</FileTitle>
<div className="post__info">
<CreditAmount amount={formattedAmount} />
<FileViewCount uri={uri} />
</div>
<FileAuthor uri={uri} />
<FileRenderInitiator uri={uri} />
<FileRenderInline uri={uri} />
<FileActions uri={uri} />
</div>
);
}
export default PostViewer;

View file

@ -3,7 +3,6 @@
import React from 'react';
import LoadingScreen from 'component/common/loading-screen';
import MarkdownPreview from 'component/common/markdown-preview';
import Card from 'component/common/card';
import CodeViewer from 'component/viewers/codeViewer';
import * as RENDER_MODES from 'constants/file_render_modes';
import * as https from 'https';
@ -42,7 +41,7 @@ class DocumentViewer extends React.PureComponent<Props, State> {
let data = '';
stream.on('data', chunk => {
stream.on('data', (chunk) => {
data += chunk;
});
@ -57,25 +56,19 @@ class DocumentViewer extends React.PureComponent<Props, State> {
// @endif
// @if TARGET='web'
if (source && source.stream) {
https.get(
source.stream,
function(response) {
if (response.statusCode === 200) {
let data = '';
response.on('data', function(chunk) {
data += chunk;
});
response.on(
'end',
function() {
this.setState({ content: data, loading: false });
}.bind(this)
);
} else {
this.setState({ error: true, loading: false });
}
}.bind(this)
);
https.get(source.stream, (response) => {
if (response.statusCode === 200) {
let data = '';
response.on('data', (chunk) => {
data += chunk;
});
response.on('end', () => {
this.setState({ content: data, loading: false });
});
} else {
this.setState({ error: true, loading: false });
}
});
}
// @endif
}
@ -86,7 +79,7 @@ class DocumentViewer extends React.PureComponent<Props, State> {
const { contentType } = source;
return renderMode === RENDER_MODES.MARKDOWN ? (
<Card body={<MarkdownPreview content={content} isMarkdownPost promptLinks />} />
<MarkdownPreview content={content} isMarkdownPost promptLinks />
) : (
<CodeViewer value={content} contentType={contentType} theme={theme} />
);

View file

@ -3,12 +3,13 @@ import * as React from 'react';
import classnames from 'classnames';
import Page from 'component/page';
import * as RENDER_MODES from 'constants/file_render_modes';
import FileTitle from 'component/fileTitle';
import FileTitleSection from 'component/fileTitleSection';
import FileRenderInitiator from 'component/fileRenderInitiator';
import FileRenderInline from 'component/fileRenderInline';
import FileRenderDownload from 'component/fileRenderDownload';
import RecommendedContent from 'component/recommendedContent';
import CommentsList from 'component/commentsList';
import PostViewer from 'component/postViewer';
import Empty from 'component/common/empty';
export const PRIMARY_PLAYER_WRAPPER_CLASS = 'file-page__video-container';
@ -17,9 +18,9 @@ type Props = {
costInfo: ?{ includesData: boolean, cost: number },
fileInfo: FileListItem,
uri: string,
fetchFileInfo: string => void,
fetchCostInfo: string => void,
setViewed: string => void,
fetchFileInfo: (string) => void,
fetchCostInfo: (string) => void,
setViewed: (string) => void,
renderMode: string,
obscureNsfw: boolean,
isMature: boolean,
@ -47,6 +48,7 @@ function FilePage(props: Props) {
} = props;
const cost = costInfo ? costInfo.cost : null;
const hasFileInfo = fileInfo !== undefined;
const isText = RENDER_MODES.TEXT_MODES.includes(renderMode);
React.useEffect(() => {
// always refresh file info when entering file page to see if we have the file
@ -82,27 +84,21 @@ function FilePage(props: Props) {
if (RENDER_MODES.UNRENDERABLE_MODES.includes(renderMode)) {
return (
<React.Fragment>
<FileTitle uri={uri} />
<FileTitleSection uri={uri} />
<FileRenderDownload uri={uri} isFree={cost === 0} />
</React.Fragment>
);
}
if (RENDER_MODES.TEXT_MODES.includes(renderMode)) {
return (
<React.Fragment>
<FileTitle uri={uri} />
<FileRenderInitiator uri={uri} />
<FileRenderInline uri={uri} />
</React.Fragment>
);
return <PostViewer uri={uri} />;
}
return (
<React.Fragment>
<FileRenderInitiator uri={uri} videoTheaterMode={videoTheaterMode} />
<FileRenderInline uri={uri} />
<FileTitle uri={uri} />
<FileTitleSection uri={uri} />
</React.Fragment>
);
}
@ -110,27 +106,38 @@ function FilePage(props: Props) {
if (obscureNsfw && isMature) {
return (
<Page>
<FileTitle uri={uri} isNsfwBlocked />
<FileTitleSection uri={uri} isNsfwBlocked />
</Page>
);
}
return (
<Page className="file-page" filePage>
<div className={classnames('section card-stack', `file-page__${renderMode}`)}>
<Page className="file-page" filePage isText={isText}>
<div
className={classnames('section card-stack', `file-page__${renderMode}`, {
'file-page__text': isText,
})}
>
{renderFilePageLayout()}
<div className="file-page__secondary-content">
<div>
{RENDER_MODES.FLOATING_MODES.includes(renderMode) && <FileTitle uri={uri} />}
{commentsDisabled && <Empty text={__('The creator of this content has disabled comments.')} />}
{!commentsDisabled && <CommentsList uri={uri} linkedComment={linkedComment} />}
{!isText && (
<div className="file-page__secondary-content">
<div>
{RENDER_MODES.FLOATING_MODES.includes(renderMode) && <FileTitleSection uri={uri} />}
{commentsDisabled && <Empty text={__('The creator of this content has disabled comments.')} />}
{!commentsDisabled && <CommentsList uri={uri} linkedComment={linkedComment} />}
</div>
{videoTheaterMode && <RecommendedContent uri={uri} />}
</div>
{videoTheaterMode && <RecommendedContent uri={uri} />}
</div>
)}
</div>
{!videoTheaterMode && <RecommendedContent uri={uri} />}
{!isText && !videoTheaterMode && <RecommendedContent uri={uri} />}
{isText && (
<div className="file-page__post-comments">
<CommentsList uri={uri} linkedComment={linkedComment} />
</div>
)}
</Page>
);
}

View file

@ -39,6 +39,7 @@
@import 'component/notification';
@import 'component/nudge';
@import 'component/pagination';
@import 'component/post';
@import 'component/purchase';
@import 'component/placeholder';
@import 'component/progress';

View file

@ -62,6 +62,7 @@
}
.claim-preview__wrapper {
@include font-sans;
padding: var(--spacing-m);
list-style: none;

View file

@ -124,6 +124,10 @@
}
}
.content__cover--text {
margin: 10rem 0;
}
.content__cover--theater-mode {
@extend .content__cover;
border-radius: 0;
@ -174,8 +178,8 @@
background-color: #000;
&.content__loading--document {
background-color: var(--color-card-background);
padding: calc(var(--spacing-xl) * 3) 0;
background-color: var(--color-background);
padding: calc(var(--spacing-xl) * 5) 0;
.content__loading-text {
color: var(--color-text);

View file

@ -12,7 +12,7 @@
margin-top: var(--spacing-l);
}
.file-page__md {
.file-page__text {
.card {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
@ -22,16 +22,14 @@
}
}
.card + .file-render {
margin-top: 0;
.media__actions {
justify-content: center;
}
.card {
border-top-left-radius: 0;
border-top-right-radius: 0;
border-bottom-left-radius: var(--border-radius);
border-bottom-right-radius: var(--border-radius);
border-top: none;
}
.file-page__secondary-content {
display: flex;
flex-direction: column;
padding: 0 var(--spacing-m);
}
}
@ -85,27 +83,6 @@
max-height: none;
}
.file-render--document {
height: auto;
max-height: none;
overflow: auto;
margin-bottom: var(--spacing-l);
.markdown-preview {
height: 100%;
width: 40rem;
margin-left: auto;
margin-right: auto;
max-width: unset;
min-width: unset;
@media (max-width: $breakpoint-small) {
width: 100%;
padding: var(--spacing-s);
}
}
}
.file-render__header {
display: flex;
justify-content: space-between;
@ -155,6 +132,14 @@
}
}
.file-viewer--document {
margin-top: var(--spacing-l);
@media (min-width: $breakpoint-small) {
margin-top: var(--spacing-xl);
}
}
.file-render__viewer--three {
position: relative;
overflow: hidden;

View file

@ -36,7 +36,6 @@
.main-wrapper__inner--filepage {
padding: 0;
padding-top: var(--spacing-s);
}
.main-wrapper__inner--theater-mode {
@ -65,10 +64,11 @@
max-width: var(--page-max-width--filepage);
margin-left: auto;
margin-right: auto;
margin-top: var(--spacing-m);
padding: 0 var(--spacing-m);
display: flex;
flex-direction: row;
align-items: flex-start;
padding-left: var(--spacing-m);
padding-right: var(--spacing-m);
position: relative;
> :first-child {
@ -77,7 +77,7 @@
.file-page__secondary-content {
display: flex;
flex-direction: column;
flex-direction: row;
justify-content: center;
width: 100%;
margin-top: var(--spacing-m);
@ -94,6 +94,14 @@
}
}
.file-page__post-comments {
margin-top: var(--spacing-l);
@media (min-width: $breakpoint-small) {
padding: var(--spacing-m);
}
}
.file-page__info {
margin-top: var(--spacing-m);
}
@ -121,6 +129,7 @@
@media (max-width: $breakpoint-small) {
padding: var(--spacing-xs);
flex-direction: column;
padding-top: 0;
}
}
@ -183,6 +192,10 @@
}
}
.main--text {
flex-direction: column;
}
.main__auth-content {
display: flex;
position: relative;

View file

@ -49,6 +49,7 @@
.media__subtitle--centered {
@extend .media__subtitle;
align-self: auto;
align-items: center;
display: flex;
}
@ -110,6 +111,7 @@
}
.media__actions {
@include font-sans;
position: relative;
display: flex;
flex-wrap: wrap;

View file

@ -0,0 +1,84 @@
.post {
$card-bg: var(--color-card-background);
$bg: var(--color-background);
@include font-serif;
.card {
border: 0;
&:after {
content: '';
display: block;
height: 20rem;
background-image: linear-gradient(to top, $bg, $card-bg);
}
}
}
.post {
height: 100%;
width: 43rem;
margin-left: auto;
margin-right: auto;
max-width: unset;
min-width: unset;
.channel-staked__wrapper {
background-color: var(--color-background);
}
.channel-thumbnail {
@include handleChannelGif(2.5rem);
}
@media (max-width: $breakpoint-small) {
width: 100%;
padding: var(--spacing-s);
}
}
.post__title {
font-size: 2rem;
line-height: 1.2;
margin-top: 0;
margin-bottom: var(--spacing-m);
font-weight: var(--font-weight-bold);
:first-child {
display: inline-block;
margin-right: var(--spacing-s);
}
@media (min-width: $breakpoint-small) {
margin-top: var(--spacing-xl);
font-size: 3rem;
line-height: 1;
}
}
.post__info {
@include font-sans;
display: flex;
align-items: center;
justify-content: space-between;
line-height: 0;
margin-bottom: var(--spacing-l);
font-size: var(--font-small);
.credit-amount {
margin-right: var(--spacing-s);
}
}
.post__date {
font-size: var(--font-small);
color: var(--color-help);
}
.file-render--document {
font-size: var(--font-large);
height: auto;
max-height: none;
overflow: auto;
margin-bottom: var(--spacing-l);
}

View file

@ -115,7 +115,6 @@
display: flex;
align-items: center;
margin-top: var(--spacing-l);
margin-right: var(--spacing-s);
~ .section {
margin-top: var(--spacing-l);