align FileActions with odysee style
This commit is contained in:
parent
0138eccb60
commit
a92a3305b1
14 changed files with 166 additions and 94 deletions
|
@ -1,7 +1,6 @@
|
||||||
// @flow
|
// @flow
|
||||||
import { SIMPLE_SITE, SITE_NAME } from 'config';
|
import { SIMPLE_SITE, SITE_NAME } from 'config';
|
||||||
import * as PAGES from 'constants/pages';
|
import * as PAGES from 'constants/pages';
|
||||||
import * as CS from 'constants/claim_search';
|
|
||||||
import * as MODALS from 'constants/modal_types';
|
import * as MODALS from 'constants/modal_types';
|
||||||
import * as ICONS from 'constants/icons';
|
import * as ICONS from 'constants/icons';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
@ -80,32 +79,23 @@ function FileActions(props: Props) {
|
||||||
|
|
||||||
const lhsSection = (
|
const lhsSection = (
|
||||||
<>
|
<>
|
||||||
|
<ClaimSupportButton uri={uri} fileAction />
|
||||||
<Button
|
<Button
|
||||||
button="alt"
|
button="alt"
|
||||||
|
className="button--file-action"
|
||||||
|
icon={ICONS.REPOST}
|
||||||
|
label={
|
||||||
|
claim.meta.reposted > 1 ? __(`%repost_total% Reposts`, { repost_total: claim.meta.reposted }) : __('Repost')
|
||||||
|
}
|
||||||
|
requiresAuth={IS_WEB}
|
||||||
|
onClick={handleRepostClick}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
className="button--file-action"
|
||||||
icon={ICONS.SHARE}
|
icon={ICONS.SHARE}
|
||||||
label={__('Share')}
|
label={__('Share')}
|
||||||
onClick={() => openModal(MODALS.SOCIAL_SHARE, { uri, webShareable })}
|
onClick={() => openModal(MODALS.SOCIAL_SHARE, { uri, webShareable })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="button-group">
|
|
||||||
<Button
|
|
||||||
button="alt"
|
|
||||||
icon={ICONS.REPOST}
|
|
||||||
label={__('Repost')}
|
|
||||||
requiresAuth={IS_WEB}
|
|
||||||
onClick={handleRepostClick}
|
|
||||||
/>
|
|
||||||
{claim.meta.reposted > 0 && (
|
|
||||||
<Button
|
|
||||||
button="alt"
|
|
||||||
label={claim.meta.reposted}
|
|
||||||
requiresAuth={IS_WEB}
|
|
||||||
navigate={`/$/${PAGES.DISCOVER}?${CS.REPOSTED_URI_KEY}=${encodeURIComponent(uri)}`}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ClaimSupportButton uri={uri} />
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -115,7 +105,7 @@ function FileActions(props: Props) {
|
||||||
|
|
||||||
{claimIsMine && (
|
{claimIsMine && (
|
||||||
<Button
|
<Button
|
||||||
button="alt"
|
className="button--file-action"
|
||||||
icon={ICONS.EDIT}
|
icon={ICONS.EDIT}
|
||||||
label={__('Edit')}
|
label={__('Edit')}
|
||||||
navigate="/$/upload"
|
navigate="/$/upload"
|
||||||
|
@ -128,16 +118,16 @@ function FileActions(props: Props) {
|
||||||
{showDelete && (
|
{showDelete && (
|
||||||
<Button
|
<Button
|
||||||
title={__('Remove from your library')}
|
title={__('Remove from your library')}
|
||||||
button="alt"
|
className="button--file-action"
|
||||||
icon={ICONS.DELETE}
|
icon={ICONS.DELETE}
|
||||||
description={__('Delete')}
|
description={__('Delete')}
|
||||||
onClick={() => openModal(MODALS.CONFIRM_FILE_REMOVE, { uri })}
|
onClick={() => openModal(MODALS.CONFIRM_FILE_REMOVE, { uri })}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{!claimIsMine && !SIMPLE_SITE && (
|
{!claimIsMine && (
|
||||||
<Button
|
<Button
|
||||||
title={__('Report content')}
|
title={__('Report content')}
|
||||||
button="alt"
|
className="button--file-action"
|
||||||
icon={ICONS.REPORT}
|
icon={ICONS.REPORT}
|
||||||
href={`https://lbry.com/dmca/${claimId}`}
|
href={`https://lbry.com/dmca/${claimId}`}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
// @flow
|
// @flow
|
||||||
import React, { PureComponent } from 'react';
|
import React from 'react';
|
||||||
|
import classnames from 'classnames';
|
||||||
import MarkdownPreview from 'component/common/markdown-preview';
|
import MarkdownPreview from 'component/common/markdown-preview';
|
||||||
import ClaimTags from 'component/claimTags';
|
import ClaimTags from 'component/claimTags';
|
||||||
import Card from 'component/common/card';
|
import Button from 'component/button';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
uri: string,
|
uri: string,
|
||||||
|
@ -12,35 +13,50 @@ type Props = {
|
||||||
tags: any,
|
tags: any,
|
||||||
};
|
};
|
||||||
|
|
||||||
class FileDescription extends PureComponent<Props> {
|
function FileDescription(props: Props) {
|
||||||
render() {
|
const { uri, claim, metadata, tags } = props;
|
||||||
const { uri, claim, metadata, tags } = this.props;
|
const [expanded, setExpanded] = React.useState(false);
|
||||||
|
const [hasOverflow, setHasOverflow] = React.useState(false);
|
||||||
|
const [hasCalculatedOverflow, setHasCalculatedOverflow] = React.useState(false);
|
||||||
|
const descriptionRef = React.useRef();
|
||||||
|
|
||||||
if (!claim || !metadata) {
|
React.useEffect(() => {
|
||||||
return <span className="empty">{__('Empty claim or metadata info.')}</span>;
|
if (descriptionRef && descriptionRef.current) {
|
||||||
|
const element = descriptionRef.current;
|
||||||
|
const isOverflowing = element.scrollHeight > element.clientHeight;
|
||||||
|
setHasOverflow(isOverflowing);
|
||||||
|
setHasCalculatedOverflow(true);
|
||||||
}
|
}
|
||||||
|
}, [descriptionRef]);
|
||||||
|
|
||||||
const { description } = metadata;
|
if (!claim || !metadata) {
|
||||||
|
return <span className="empty">{__('Empty claim or metadata info.')}</span>;
|
||||||
if (!description && !(tags && tags.length)) return null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card
|
|
||||||
title={__('Description')}
|
|
||||||
defaultExpand
|
|
||||||
actions={
|
|
||||||
<>
|
|
||||||
{description && (
|
|
||||||
<div className="media__info-text">
|
|
||||||
<MarkdownPreview className="markdown-preview--description" content={description} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<ClaimTags uri={uri} type="large" />
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { description } = metadata;
|
||||||
|
|
||||||
|
if (!description && !(tags && tags.length)) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
ref={descriptionRef}
|
||||||
|
className={classnames({ 'media__info-text--contracted': !expanded, 'media__info-text--expanded': expanded })}
|
||||||
|
>
|
||||||
|
<MarkdownPreview className="markdown-preview--description" content={description} />
|
||||||
|
<ClaimTags uri={uri} type="large" />
|
||||||
|
</div>
|
||||||
|
{hasCalculatedOverflow && hasOverflow && (
|
||||||
|
<div className="media__info-expand">
|
||||||
|
{expanded ? (
|
||||||
|
<Button button="link" label={__('Less')} onClick={() => setExpanded(!expanded)} />
|
||||||
|
) : (
|
||||||
|
<Button button="link" label={__('More')} onClick={() => setExpanded(!expanded)} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default FileDescription;
|
export default FileDescription;
|
||||||
|
|
|
@ -33,7 +33,7 @@ function FileDownloadLink(props: Props) {
|
||||||
download,
|
download,
|
||||||
uri,
|
uri,
|
||||||
claim,
|
claim,
|
||||||
buttonType = 'alt',
|
buttonType,
|
||||||
showLabel = false,
|
showLabel = false,
|
||||||
hideOpenButton = false,
|
hideOpenButton = false,
|
||||||
hideDownloadStatus = false,
|
hideDownloadStatus = false,
|
||||||
|
@ -107,6 +107,7 @@ function FileDownloadLink(props: Props) {
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
button={buttonType}
|
button={buttonType}
|
||||||
|
className={buttonType ? undefined : 'button--file-action'}
|
||||||
title={label}
|
title={label}
|
||||||
icon={ICONS.DOWNLOAD}
|
icon={ICONS.DOWNLOAD}
|
||||||
label={showLabel ? label : null}
|
label={showLabel ? label : null}
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { makeSelectClaimForUri, makeSelectPendingAmountByUri, makeSelectClaimIsMine } from 'lbry-redux';
|
import { makeSelectClaimForUri, makeSelectPendingAmountByUri } from 'lbry-redux';
|
||||||
import FileSubtitle from './view';
|
import FileSubtitle from './view';
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
claim: makeSelectClaimForUri(props.uri)(state),
|
claim: makeSelectClaimForUri(props.uri)(state),
|
||||||
pendingAmount: makeSelectPendingAmountByUri(props.uri)(state),
|
pendingAmount: makeSelectPendingAmountByUri(props.uri)(state),
|
||||||
claimIsMine: makeSelectClaimIsMine(props.uri)(state),
|
|
||||||
});
|
});
|
||||||
export default connect(select)(FileSubtitle);
|
export default connect(select)(FileSubtitle);
|
||||||
|
|
|
@ -1,41 +1,40 @@
|
||||||
// @flow
|
// @flow
|
||||||
import { SIMPLE_SITE } from 'config';
|
import { SIMPLE_SITE } from 'config';
|
||||||
import * as ICONS from 'constants/icons';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import DateTime from 'component/dateTime';
|
import DateTime from 'component/dateTime';
|
||||||
import FileViewCount from 'component/fileViewCount';
|
import FileViewCount from 'component/fileViewCount';
|
||||||
import CreditAmount from 'component/common/credit-amount';
|
import CreditAmount from 'component/common/credit-amount';
|
||||||
import HelpLink from 'component/common/help-link';
|
import FileActions from 'component/fileActions';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
uri: string,
|
uri: string,
|
||||||
claim: StreamClaim,
|
claim: StreamClaim,
|
||||||
pendingAmount: string,
|
pendingAmount: string,
|
||||||
claimIsMine: boolean,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function FileSubtitle(props: Props) {
|
function FileSubtitle(props: Props) {
|
||||||
const { uri, claim, pendingAmount, claimIsMine } = props;
|
const { uri, claim, pendingAmount } = props;
|
||||||
const claimId = claim && claim.claim_id;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="media__subtitle--between">
|
<div className="media__subtitle--between">
|
||||||
<DateTime uri={uri} show={DateTime.SHOW_DATE} />
|
<div className="file__viewdate">
|
||||||
<span>
|
<span>
|
||||||
{!SIMPLE_SITE && (
|
<DateTime uri={uri} show={DateTime.SHOW_DATE} />
|
||||||
<>
|
{!SIMPLE_SITE && (
|
||||||
<CreditAmount
|
<>
|
||||||
amount={parseFloat(claim.amount) + parseFloat(pendingAmount || claim.meta.support_amount)}
|
{' • ' /* this is bad, but it's quick! */}
|
||||||
precision={2}
|
<CreditAmount
|
||||||
/>
|
amount={parseFloat(claim.amount) + parseFloat(pendingAmount || claim.meta.support_amount)}
|
||||||
{' • ' /* this is bad, but it's quick! */}
|
precision={2}
|
||||||
</>
|
/>
|
||||||
)}
|
</>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
|
||||||
<FileViewCount uri={uri} />
|
<FileViewCount uri={uri} />
|
||||||
{claimId && !claimIsMine && SIMPLE_SITE && (
|
</div>
|
||||||
<HelpLink description={__('Report content')} icon={ICONS.REPORT} href={`https://lbry.com/dmca/${claimId}`} />
|
|
||||||
)}
|
<FileActions uri={uri} />
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,13 +5,13 @@ import FilePrice from 'component/filePrice';
|
||||||
import ClaimInsufficientCredits from 'component/claimInsufficientCredits';
|
import ClaimInsufficientCredits from 'component/claimInsufficientCredits';
|
||||||
import FileSubtitle from 'component/fileSubtitle';
|
import FileSubtitle from 'component/fileSubtitle';
|
||||||
import FileAuthor from 'component/fileAuthor';
|
import FileAuthor from 'component/fileAuthor';
|
||||||
import FileActions from 'component/fileActions';
|
|
||||||
import Card from 'component/common/card';
|
import Card from 'component/common/card';
|
||||||
import * as ICONS from 'constants/icons';
|
import * as ICONS from 'constants/icons';
|
||||||
import Icon from 'component/common/icon';
|
import Icon from 'component/common/icon';
|
||||||
import I18nMessage from 'component/i18nMessage';
|
import I18nMessage from 'component/i18nMessage';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import * as PAGES from 'constants/pages';
|
import * as PAGES from 'constants/pages';
|
||||||
|
import FileDescription from 'component/fileDescription';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
uri: string,
|
uri: string,
|
||||||
|
@ -64,13 +64,9 @@ function FileTitle(props: Props) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div className="section">
|
||||||
<div className="section">
|
<FileAuthor uri={uri} />
|
||||||
<FileActions uri={uri} />
|
<FileDescription uri={uri} />
|
||||||
</div>
|
|
||||||
<div className="section">
|
|
||||||
<FileAuthor uri={uri} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ function FileViewCount(props: Props) {
|
||||||
const formattedViewCount = Number(viewCount).toLocaleString();
|
const formattedViewCount = Number(viewCount).toLocaleString();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span>
|
<span className="media__subtitle--centered">
|
||||||
{viewCount !== 1 ? __('%view_count% views', { view_count: formattedViewCount }) : __('1 view')}
|
{viewCount !== 1 ? __('%view_count% views', { view_count: formattedViewCount }) : __('1 view')}
|
||||||
<HelpLink href="https://lbry.com/faq/views" />
|
<HelpLink href="https://lbry.com/faq/views" />
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -9,8 +9,6 @@ import FileRenderInline from 'component/fileRenderInline';
|
||||||
import FileRenderDownload from 'component/fileRenderDownload';
|
import FileRenderDownload from 'component/fileRenderDownload';
|
||||||
import FileDetails from 'component/fileDetails';
|
import FileDetails from 'component/fileDetails';
|
||||||
import FileValues from 'component/fileValues';
|
import FileValues from 'component/fileValues';
|
||||||
import FileDescription from 'component/fileDescription';
|
|
||||||
// import WaitUntilOnPage from 'component/common/wait-until-on-page';
|
|
||||||
import RecommendedContent from 'component/recommendedContent';
|
import RecommendedContent from 'component/recommendedContent';
|
||||||
import CommentsList from 'component/commentsList';
|
import CommentsList from 'component/commentsList';
|
||||||
|
|
||||||
|
@ -145,7 +143,6 @@ class FilePage extends React.Component<Props> {
|
||||||
<Page className="file-page" filePage>
|
<Page className="file-page" filePage>
|
||||||
<div className={classnames('section card-stack', `file-page__${renderMode}`)}>
|
<div className={classnames('section card-stack', `file-page__${renderMode}`)}>
|
||||||
{this.renderFilePageLayout(uri, renderMode, costInfo ? costInfo.cost : null)}
|
{this.renderFilePageLayout(uri, renderMode, costInfo ? costInfo.cost : null)}
|
||||||
<FileDescription uri={uri} />
|
|
||||||
<FileValues uri={uri} />
|
<FileValues uri={uri} />
|
||||||
<FileDetails uri={uri} />
|
<FileDetails uri={uri} />
|
||||||
|
|
||||||
|
|
|
@ -167,6 +167,33 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button--file-action {
|
||||||
|
@extend .button--alt;
|
||||||
|
background-color: transparent;
|
||||||
|
margin-right: var(--spacing-m);
|
||||||
|
padding: 0 var(--spacing-xxs);
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
stroke: #777;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button__label {
|
||||||
|
min-width: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--color-button-alt-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.button--disabled {
|
.button--disabled {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
|
@ -173,13 +173,10 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: $breakpoint-xsmall) {
|
@media (max-width: $breakpoint-xsmall) {
|
||||||
.claim-preview__text {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.claim-preview__actions {
|
.claim-preview__actions {
|
||||||
align-self: flex-start;
|
align-self: flex-start;
|
||||||
}
|
}
|
||||||
|
|
|
@ -497,3 +497,18 @@
|
||||||
.autoplay-countdown__button--1 {
|
.autoplay-countdown__button--1 {
|
||||||
border-color: #fff;
|
border-color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.file__viewdate {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: var(--spacing-s);
|
||||||
|
|
||||||
|
@media (min-width: $breakpoint-small) {
|
||||||
|
flex-direction: column;
|
||||||
|
margin-bottom: 0;
|
||||||
|
|
||||||
|
> :first-child {
|
||||||
|
margin-bottom: var(--spacing-s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -37,7 +37,6 @@
|
||||||
color: var(--color-subtitle);
|
color: var(--color-subtitle);
|
||||||
margin-left: var(--spacing-xs);
|
margin-left: var(--spacing-xs);
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
top: 2px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon--hidden {
|
.icon--hidden {
|
||||||
|
|
|
@ -69,11 +69,22 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.media__subtitle--centered {
|
||||||
|
@extend .media__subtitle;
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
.media__subtitle--between {
|
.media__subtitle--between {
|
||||||
@extend .media__subtitle;
|
@extend .media__subtitle;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
flex-direction: column;
|
||||||
align-items: center;
|
|
||||||
|
@media (min-width: $breakpoint-small) {
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-end;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.media__info-text {
|
.media__info-text {
|
||||||
|
@ -88,12 +99,36 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.media__info-text--contracted {
|
||||||
|
margin-top: var(--spacing-m);
|
||||||
|
max-height: 5rem;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media__info-text--expanded {
|
||||||
|
margin-top: var(--spacing-m);
|
||||||
|
max-height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media__info-text--contracted,
|
||||||
|
.media__info-text--expanded {
|
||||||
|
max-width: 50rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media__info-expand {
|
||||||
|
margin-top: var(--spacing-s);
|
||||||
|
}
|
||||||
|
|
||||||
.media__actions {
|
.media__actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
|
|
||||||
|
> *:not(:last-child) {
|
||||||
|
margin-right: var(--spacing-m);
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: $breakpoint-small) {
|
@media (max-width: $breakpoint-small) {
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
padding-top: var(--spacing-s);
|
padding-top: var(--spacing-s);
|
||||||
|
|
|
@ -160,6 +160,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.section__actions--no-margin {
|
.section__actions--no-margin {
|
||||||
|
@extend .section__actions;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue