Merge pull request #3933 from lbryio/feat-expandingCards
Tip unlock modal Expanding cards
This commit is contained in:
commit
48d03019d6
29 changed files with 645 additions and 127 deletions
|
@ -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#90ba18d0602956016ded63e816734713cfd2023a",
|
"lbry-redux": "lbryio/lbry-redux#1097a63d44a20b87e443fbaa48f95fe3ea5e3f70",
|
||||||
"lbryinc": "lbryio/lbryinc#19260fac560daaa4be2d4af372f28109ea96ebf9",
|
"lbryinc": "lbryio/lbryinc#19260fac560daaa4be2d4af372f28109ea96ebf9",
|
||||||
"lint-staged": "^7.0.2",
|
"lint-staged": "^7.0.2",
|
||||||
"localforage": "^1.7.1",
|
"localforage": "^1.7.1",
|
||||||
|
|
|
@ -335,6 +335,7 @@
|
||||||
"credits": "credits",
|
"credits": "credits",
|
||||||
"No channel name after @.": "No channel name after @.",
|
"No channel name after @.": "No channel name after @.",
|
||||||
"View channel": "View channel",
|
"View channel": "View channel",
|
||||||
|
"Add to your library": "Add to your library",
|
||||||
"Web link": "Web link",
|
"Web link": "Web link",
|
||||||
"Facebook": "Facebook",
|
"Facebook": "Facebook",
|
||||||
"Twitter": "Twitter",
|
"Twitter": "Twitter",
|
||||||
|
@ -841,6 +842,7 @@
|
||||||
"Any amount will give you the highest bid, but larger amounts help your content be trusted and discovered.": "Any amount will give you the highest bid, but larger amounts help your content be trusted and discovered.",
|
"Any amount will give you the highest bid, but larger amounts help your content be trusted and discovered.": "Any amount will give you the highest bid, but larger amounts help your content be trusted and discovered.",
|
||||||
"Loading 3D model.": "Loading 3D model.",
|
"Loading 3D model.": "Loading 3D model.",
|
||||||
"Click here": "Click here",
|
"Click here": "Click here",
|
||||||
|
"PDF opened externally. %click_here% to open it again.": "PDF opened externally. %click_here% to open it again.",
|
||||||
"Wallet Server": "Wallet Server",
|
"Wallet Server": "Wallet Server",
|
||||||
"lbry.tv wallet servers": "lbry.tv wallet servers",
|
"lbry.tv wallet servers": "lbry.tv wallet servers",
|
||||||
"Custom wallet servers": "Custom wallet servers",
|
"Custom wallet servers": "Custom wallet servers",
|
||||||
|
@ -997,7 +999,7 @@
|
||||||
"Short": "Short",
|
"Short": "Short",
|
||||||
"How Fresh": "How Fresh",
|
"How Fresh": "How Fresh",
|
||||||
"This Default": "This Default",
|
"This Default": "This Default",
|
||||||
"Sorry, your request timed out. Modify your options or %again%": "Sorry, your request timed out. Modify your options or %again%",
|
"Sorry, your request returned no results or timed out. Modify your options or %again%": "Sorry, your request returned no results or timed out. Modify your options or %again%",
|
||||||
"Image": "Image",
|
"Image": "Image",
|
||||||
"Model": "Model",
|
"Model": "Model",
|
||||||
"Binary": "Binary",
|
"Binary": "Binary",
|
||||||
|
@ -1008,7 +1010,6 @@
|
||||||
"Only apply a few tags that are relevant to your content, and use the Mature tag as appropriate. Tag abuse will not be tolerated.": "Only apply a few tags that are relevant to your content, and use the Mature tag as appropriate. Tag abuse will not be tolerated.",
|
"Only apply a few tags that are relevant to your content, and use the Mature tag as appropriate. Tag abuse will not be tolerated.": "Only apply a few tags that are relevant to your content, and use the Mature tag as appropriate. Tag abuse will not be tolerated.",
|
||||||
"Add relevant tags...": "Add relevant tags...",
|
"Add relevant tags...": "Add relevant tags...",
|
||||||
"Enter up to five (5) tags that are relevant to your content, and use the Mature tag as appropriate. Tag abuse will not be tolerated.": "Enter up to five (5) tags that are relevant to your content, and use the Mature tag as appropriate. Tag abuse will not be tolerated.",
|
"Enter up to five (5) tags that are relevant to your content, and use the Mature tag as appropriate. Tag abuse will not be tolerated.": "Enter up to five (5) tags that are relevant to your content, and use the Mature tag as appropriate. Tag abuse will not be tolerated.",
|
||||||
"Sorry, your request timed out. Modify your options or %again%": "Sorry, your request timed out. Modify your options or %again%",
|
|
||||||
"gaming, crypto": "gaming, crypto",
|
"gaming, crypto": "gaming, crypto",
|
||||||
"Autocomplete": "Autocomplete",
|
"Autocomplete": "Autocomplete",
|
||||||
"Followed Tags": "Followed Tags",
|
"Followed Tags": "Followed Tags",
|
||||||
|
@ -1084,5 +1085,37 @@
|
||||||
"Kannada": "Kannada",
|
"Kannada": "Kannada",
|
||||||
"Transcoding this %size%MB file should take under %processTime% %units%.": "Transcoding this %size%MB file should take under %processTime% %units%.",
|
"Transcoding this %size%MB file should take under %processTime% %units%.": "Transcoding this %size%MB file should take under %processTime% %units%.",
|
||||||
"FFmpeg not configured. More in %settings_link%.": "FFmpeg not configured. More in %settings_link%.",
|
"FFmpeg not configured. More in %settings_link%.": "FFmpeg not configured. More in %settings_link%.",
|
||||||
"smallresult": "smallresult"
|
"File Details": "File Details",
|
||||||
|
"LBC Details": "LBC Details",
|
||||||
|
"Publish Amount": "Publish Amount",
|
||||||
|
"Supports and Tips": "Supports and Tips",
|
||||||
|
"Top for name": "Top for name",
|
||||||
|
"%name%": "%name%",
|
||||||
|
"Amount must be a number": "Amount must be a number",
|
||||||
|
"Amount cannot be blank": "Amount cannot be blank",
|
||||||
|
"Amount cannot be more than available": "Amount cannot be more than available",
|
||||||
|
"Amount to unlock": "Amount to unlock",
|
||||||
|
"Unlock Tips": "Unlock Tips",
|
||||||
|
"available to unlock.": "available to unlock.",
|
||||||
|
"%message%": "%message%",
|
||||||
|
"Support This Claim": "Support This Claim",
|
||||||
|
"view other claims at lbry://%name%": "view other claims at lbry://%name%",
|
||||||
|
"Not top for name": "Not top for name",
|
||||||
|
"loading": "loading",
|
||||||
|
"Original Publish Amount": "Original Publish Amount",
|
||||||
|
"Total Staked Amount": "Total Staked Amount",
|
||||||
|
"Set Inviter": "Set Inviter",
|
||||||
|
"Sign In to lbry.tv to Earn Rewards From Inviting Your Friends": "Sign In to lbry.tv to Earn Rewards From Inviting Your Friends",
|
||||||
|
"You haven't published anything with this channel yet!": "You haven't published anything with this channel yet!",
|
||||||
|
"Publish Something": "Publish Something",
|
||||||
|
"Amount cannot be zero": "Amount cannot be zero",
|
||||||
|
"Your content will do better with more staked on it": "Your content will do better with more staked on it",
|
||||||
|
"She's about to close up the library!": "She's about to close up the library!",
|
||||||
|
"Community Choice?": "Community Choice?",
|
||||||
|
"Download to your Library": "Download to your Library",
|
||||||
|
"Leave a Comment": "Leave a Comment",
|
||||||
|
"Repost %count%": "Repost %count%",
|
||||||
|
"File Description": "File Description",
|
||||||
|
"View %count% reposts": "View %count% reposts",
|
||||||
|
"Preparing your content": "Preparing your content"
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,6 @@ function ChannelForm(props: Props) {
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<Card
|
<Card
|
||||||
actionIconPadding={false}
|
|
||||||
icon={ICONS.CHANNEL}
|
icon={ICONS.CHANNEL}
|
||||||
title="Create a New Channel"
|
title="Create a New Channel"
|
||||||
subtitle="This is a username or handle that your content can be found under."
|
subtitle="This is a username or handle that your content can be found under."
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
// @flow
|
// @flow
|
||||||
import type { Node } from 'react';
|
import type { Node } from 'react';
|
||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import Icon from 'component/common/icon';
|
import Icon from 'component/common/icon';
|
||||||
|
import Button from 'component/button';
|
||||||
|
import { toCapitalCase } from 'util/string';
|
||||||
|
import * as ICONS from 'constants/icons';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
title?: string | Node,
|
title?: string | Node,
|
||||||
|
@ -13,7 +16,7 @@ type Props = {
|
||||||
className?: string,
|
className?: string,
|
||||||
isPageTitle?: boolean,
|
isPageTitle?: boolean,
|
||||||
isBodyTable?: boolean,
|
isBodyTable?: boolean,
|
||||||
actionIconPadding?: boolean,
|
defaultExpand?: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Card(props: Props) {
|
export default function Card(props: Props) {
|
||||||
|
@ -26,12 +29,16 @@ export default function Card(props: Props) {
|
||||||
className,
|
className,
|
||||||
isPageTitle = false,
|
isPageTitle = false,
|
||||||
isBodyTable = false,
|
isBodyTable = false,
|
||||||
actionIconPadding = true,
|
defaultExpand,
|
||||||
} = props;
|
} = props;
|
||||||
|
const [expanded, setExpanded] = useState(defaultExpand);
|
||||||
|
const expandable = defaultExpand !== undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={classnames(className, 'card')}>
|
<section className={classnames(className, 'card')}>
|
||||||
{(title || subtitle) && (
|
{(title || subtitle) && (
|
||||||
<div className="card__header">
|
<div className="card__header--between">
|
||||||
|
<div className="card__section--flex">
|
||||||
{icon && <Icon sectionIcon icon={icon} />}
|
{icon && <Icon sectionIcon icon={icon} />}
|
||||||
<div>
|
<div>
|
||||||
{isPageTitle && <h1 className="card__title">{title}</h1>}
|
{isPageTitle && <h1 className="card__title">{title}</h1>}
|
||||||
|
@ -39,12 +46,23 @@ export default function Card(props: Props) {
|
||||||
{subtitle && <div className="card__subtitle">{subtitle}</div>}
|
{subtitle && <div className="card__subtitle">{subtitle}</div>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{expandable && (
|
||||||
|
<div className="section--padded">
|
||||||
|
<Button
|
||||||
|
button={'alt'}
|
||||||
|
aria-label={__('More')}
|
||||||
|
icon={expanded ? toCapitalCase(ICONS.SUBTRACT) : toCapitalCase(ICONS.ADD)}
|
||||||
|
onClick={() => setExpanded(!expanded)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{(!expandable || (expandable && expanded)) && (
|
||||||
|
<>
|
||||||
{body && (
|
{body && (
|
||||||
<div
|
<div
|
||||||
className={classnames('card__body', {
|
className={classnames('card__body', {
|
||||||
'card__body--with-icon': icon,
|
|
||||||
'card__body--no-title': !title && !subtitle,
|
'card__body--no-title': !title && !subtitle,
|
||||||
'card__body--table': isBodyTable,
|
'card__body--table': isBodyTable,
|
||||||
})}
|
})}
|
||||||
|
@ -52,12 +70,8 @@ export default function Card(props: Props) {
|
||||||
{body}
|
{body}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{actions && (
|
{actions && <div className="card__main-actions">{actions}</div>}
|
||||||
<div
|
</>
|
||||||
className={classnames('card__main-actions', { 'card__main-actions--with-icon': icon && actionIconPadding })}
|
|
||||||
>
|
|
||||||
{actions}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|
|
@ -32,6 +32,9 @@ type Props = {
|
||||||
blockWrap: boolean,
|
blockWrap: boolean,
|
||||||
charCount?: number,
|
charCount?: number,
|
||||||
textAreaMaxLength?: number,
|
textAreaMaxLength?: number,
|
||||||
|
range?: number,
|
||||||
|
min?: number,
|
||||||
|
max?: number,
|
||||||
};
|
};
|
||||||
|
|
||||||
export class FormField extends React.PureComponent<Props> {
|
export class FormField extends React.PureComponent<Props> {
|
||||||
|
@ -98,6 +101,13 @@ export class FormField extends React.PureComponent<Props> {
|
||||||
<label htmlFor={name}>{label}</label>
|
<label htmlFor={name}>{label}</label>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
} else if (type === 'range') {
|
||||||
|
input = (
|
||||||
|
<div>
|
||||||
|
<input id={name} type="range" {...inputProps} />
|
||||||
|
<label htmlFor={name}>{label}</label>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
} else if (type === 'select') {
|
} else if (type === 'select') {
|
||||||
input = (
|
input = (
|
||||||
<fieldset-section>
|
<fieldset-section>
|
||||||
|
|
|
@ -209,6 +209,11 @@ export const icons = {
|
||||||
<line x1="5" y1="12" x2="19" y2="12" />
|
<line x1="5" y1="12" x2="19" y2="12" />
|
||||||
</g>
|
</g>
|
||||||
),
|
),
|
||||||
|
[ICONS.SUBTRACT]: buildIcon(
|
||||||
|
<g>
|
||||||
|
<line x1="5" y1="12" x2="19" y2="12" />
|
||||||
|
</g>
|
||||||
|
),
|
||||||
[ICONS.CHAT]: buildIcon(
|
[ICONS.CHAT]: buildIcon(
|
||||||
<path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" />
|
<path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" />
|
||||||
),
|
),
|
||||||
|
@ -218,7 +223,7 @@ export const icons = {
|
||||||
[ICONS.NO]: buildIcon(
|
[ICONS.NO]: buildIcon(
|
||||||
<path d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17" />
|
<path d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17" />
|
||||||
),
|
),
|
||||||
[ICONS.UP]: buildIcon(<polyline transform="translate(-5.000) scale(1.1, 1.1)" points="18 15 12 9 6 15" />),
|
[ICONS.UP]: buildIcon(<polyline transform="matrix(1,0,0,-1,0,24.707107)" points="6 9 12 15 18 9" />),
|
||||||
[ICONS.DOWN]: buildIcon(<polyline points="6 9 12 15 18 9" />),
|
[ICONS.DOWN]: buildIcon(<polyline points="6 9 12 15 18 9" />),
|
||||||
[ICONS.FULLSCREEN]: buildIcon(
|
[ICONS.FULLSCREEN]: buildIcon(
|
||||||
<path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3" />
|
<path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3" />
|
||||||
|
|
13
ui/component/fileDescription/index.js
Normal file
13
ui/component/fileDescription/index.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { makeSelectClaimForUri, makeSelectMetadataForUri, makeSelectTagsForUri } from 'lbry-redux';
|
||||||
|
import { selectUser } from 'lbryinc';
|
||||||
|
import FileDescription from './view';
|
||||||
|
|
||||||
|
const select = (state, props) => ({
|
||||||
|
claim: makeSelectClaimForUri(props.uri)(state),
|
||||||
|
metadata: makeSelectMetadataForUri(props.uri)(state),
|
||||||
|
user: selectUser(state),
|
||||||
|
tags: makeSelectTagsForUri(props.uri)(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(select, null)(FileDescription);
|
47
ui/component/fileDescription/view.jsx
Normal file
47
ui/component/fileDescription/view.jsx
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
// @flow
|
||||||
|
import React, { Fragment, PureComponent } from 'react';
|
||||||
|
import MarkdownPreview from 'component/common/markdown-preview';
|
||||||
|
import ClaimTags from 'component/claimTags';
|
||||||
|
import Card from 'component/common/card';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
uri: string,
|
||||||
|
claim: StreamClaim,
|
||||||
|
metadata: StreamMetadata,
|
||||||
|
user: ?any,
|
||||||
|
tags: any,
|
||||||
|
};
|
||||||
|
|
||||||
|
class FileDescription extends PureComponent<Props> {
|
||||||
|
render() {
|
||||||
|
const { uri, claim, metadata, tags } = this.props;
|
||||||
|
|
||||||
|
if (!claim || !metadata) {
|
||||||
|
return <span className="empty">{__('Empty claim or metadata info.')}</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { description } = metadata;
|
||||||
|
|
||||||
|
if (!description && !(tags && tags.length)) return null;
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<Card
|
||||||
|
title={__('Description')}
|
||||||
|
defaultExpand
|
||||||
|
actions={
|
||||||
|
<>
|
||||||
|
{description && (
|
||||||
|
<div className="media__info-text">
|
||||||
|
<MarkdownPreview content={description} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<ClaimTags uri={uri} type="large" />
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FileDescription;
|
|
@ -2,11 +2,8 @@
|
||||||
import * as PAGES from 'constants/pages';
|
import * as PAGES from 'constants/pages';
|
||||||
import * as CS from 'constants/claim_search';
|
import * as CS from 'constants/claim_search';
|
||||||
import React, { Fragment, PureComponent } from 'react';
|
import React, { Fragment, PureComponent } from 'react';
|
||||||
import MarkdownPreview from 'component/common/markdown-preview';
|
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import Expandable from 'component/expandable';
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import ClaimTags from 'component/claimTags';
|
|
||||||
import Card from 'component/common/card';
|
import Card from 'component/common/card';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
@ -21,13 +18,13 @@ type Props = {
|
||||||
|
|
||||||
class FileDetails extends PureComponent<Props> {
|
class FileDetails extends PureComponent<Props> {
|
||||||
render() {
|
render() {
|
||||||
const { uri, claim, contentType, fileInfo, metadata, openFolder } = this.props;
|
const { claim, contentType, fileInfo, metadata, openFolder } = this.props;
|
||||||
|
|
||||||
if (!claim || !metadata) {
|
if (!claim || !metadata) {
|
||||||
return <span className="empty">{__('Empty claim or metadata info.')}</span>;
|
return <span className="empty">{__('Empty claim or metadata info.')}</span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { description, languages, license } = metadata;
|
const { languages, license } = metadata;
|
||||||
|
|
||||||
const mediaType = contentType || 'unknown';
|
const mediaType = contentType || 'unknown';
|
||||||
const fileSize =
|
const fileSize =
|
||||||
|
@ -46,51 +43,21 @@ class FileDetails extends PureComponent<Props> {
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<Card
|
<Card
|
||||||
title={__('Details')}
|
title={__('File Details')}
|
||||||
body={
|
defaultExpand={false}
|
||||||
<Expandable>
|
actions={
|
||||||
{description && (
|
|
||||||
<div className="media__info-text">
|
|
||||||
<MarkdownPreview content={description} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<ClaimTags uri={uri} type="large" />
|
|
||||||
<table className="table table--condensed table--fixed table--file-details">
|
<table className="table table--condensed table--fixed table--file-details">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>{__('Content Type')}</td>
|
<td> {__('Content Type')}</td>
|
||||||
<td>{mediaType}</td>
|
<td>{mediaType}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{claim && claim.meta.reposted > 0 && (
|
|
||||||
<tr>
|
|
||||||
<td>{__('Reposts')}</td>
|
|
||||||
<td>
|
|
||||||
<Button
|
|
||||||
button="link"
|
|
||||||
label={__('View %count% reposts', { count: claim.meta.reposted })}
|
|
||||||
navigate={`/$/${PAGES.DISCOVER}?${CS.REPOSTED_URI_KEY}=${encodeURIComponent(uri)}`}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
)}
|
|
||||||
{fileSize && (
|
{fileSize && (
|
||||||
<tr>
|
<tr>
|
||||||
<td> {__('File Size')}</td>
|
<td> {__('File Size')}</td>
|
||||||
<td>{fileSize}</td>
|
<td>{fileSize}</td>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
)}
|
||||||
<tr>
|
|
||||||
<td> {__('Bid Amount')}</td>
|
|
||||||
<td>{claim.amount} LBC</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td> {__('Effective Amount')}</td>
|
|
||||||
<td>{claim.meta.effective_amount} LBC</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td> {__('Is Controlling')}</td>
|
|
||||||
<td>{claim.meta.is_controlling ? __('Yes') : __('No')}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
<tr>
|
||||||
<td> {__('Claim ID')}</td>
|
<td> {__('Claim ID')}</td>
|
||||||
<td>{claim.claim_id}</td>
|
<td>{claim.claim_id}</td>
|
||||||
|
@ -126,7 +93,6 @@ class FileDetails extends PureComponent<Props> {
|
||||||
)}
|
)}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</Expandable>
|
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { makeSelectClaimForUri } 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),
|
||||||
});
|
});
|
||||||
export default connect(select)(FileSubtitle);
|
export default connect(select)(FileSubtitle);
|
||||||
|
|
|
@ -7,10 +7,11 @@ import CreditAmount from 'component/common/credit-amount';
|
||||||
type Props = {
|
type Props = {
|
||||||
uri: string,
|
uri: string,
|
||||||
claim: StreamClaim,
|
claim: StreamClaim,
|
||||||
|
pendingAmount: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
function FileSubtitle(props: Props) {
|
function FileSubtitle(props: Props) {
|
||||||
const { uri, claim } = props;
|
const { uri, claim, pendingAmount } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="media__subtitle--between">
|
<div className="media__subtitle--between">
|
||||||
|
@ -18,7 +19,7 @@ function FileSubtitle(props: Props) {
|
||||||
<span>
|
<span>
|
||||||
<CreditAmount
|
<CreditAmount
|
||||||
badge={false}
|
badge={false}
|
||||||
amount={parseFloat(claim.amount) + parseFloat(claim.meta.support_amount)}
|
amount={parseFloat(claim.amount) + parseFloat(pendingAmount || claim.meta.support_amount)}
|
||||||
precision={2}
|
precision={2}
|
||||||
/>
|
/>
|
||||||
{' • ' /* this is bad, but it's quick! */}
|
{' • ' /* this is bad, but it's quick! */}
|
||||||
|
|
29
ui/component/fileValues/index.js
Normal file
29
ui/component/fileValues/index.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import {
|
||||||
|
makeSelectClaimForUri,
|
||||||
|
makeSelectContentTypeForUri,
|
||||||
|
makeSelectMetadataForUri,
|
||||||
|
makeSelectFileInfoForUri,
|
||||||
|
makeSelectPendingAmountByUri,
|
||||||
|
makeSelectClaimIsMine,
|
||||||
|
} from 'lbry-redux';
|
||||||
|
import { selectUser } from 'lbryinc';
|
||||||
|
import { doOpenModal } from 'redux/actions/app';
|
||||||
|
|
||||||
|
import FileValues from './view';
|
||||||
|
|
||||||
|
const select = (state, props) => ({
|
||||||
|
claim: makeSelectClaimForUri(props.uri)(state),
|
||||||
|
contentType: makeSelectContentTypeForUri(props.uri)(state),
|
||||||
|
fileInfo: makeSelectFileInfoForUri(props.uri)(state),
|
||||||
|
metadata: makeSelectMetadataForUri(props.uri)(state),
|
||||||
|
user: selectUser(state),
|
||||||
|
pendingAmount: makeSelectPendingAmountByUri(props.uri)(state),
|
||||||
|
claimIsMine: makeSelectClaimIsMine(props.uri)(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
const perform = dispatch => ({
|
||||||
|
openModal: (modal, props) => dispatch(doOpenModal(modal, props)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(select, perform)(FileValues);
|
115
ui/component/fileValues/view.jsx
Normal file
115
ui/component/fileValues/view.jsx
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
// @flow
|
||||||
|
import React, { Fragment, PureComponent } from 'react';
|
||||||
|
import * as ICONS from 'constants/icons';
|
||||||
|
import * as MODALS from 'constants/modal_types';
|
||||||
|
import Button from 'component/button';
|
||||||
|
import Spinner from 'component/spinner';
|
||||||
|
import * as PAGES from 'constants/pages';
|
||||||
|
import HelpLink from 'component/common/help-link';
|
||||||
|
import CreditAmount from 'component/common/credit-amount';
|
||||||
|
import Card from 'component/common/card';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
uri: string,
|
||||||
|
claim: StreamClaim,
|
||||||
|
fileInfo: FileListItem,
|
||||||
|
metadata: StreamMetadata,
|
||||||
|
openFolder: string => void,
|
||||||
|
contentType: string,
|
||||||
|
user: ?any,
|
||||||
|
pendingAmount: string,
|
||||||
|
openModal: (id: string, { uri: string }) => void,
|
||||||
|
claimIsMine: boolean,
|
||||||
|
};
|
||||||
|
|
||||||
|
class FileValues extends PureComponent<Props> {
|
||||||
|
render() {
|
||||||
|
const { uri, claim, metadata, openModal, pendingAmount, claimIsMine } = this.props;
|
||||||
|
if (!claim || !metadata) {
|
||||||
|
return <span className="empty">{__('Empty claim or metadata info.')}</span>;
|
||||||
|
}
|
||||||
|
const supportsAmount =
|
||||||
|
claim &&
|
||||||
|
claim.meta &&
|
||||||
|
claim.amount &&
|
||||||
|
claim.meta.effective_amount &&
|
||||||
|
Number(claim.meta.effective_amount) - Number(claim.amount);
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<Card
|
||||||
|
title={__('LBC Details')}
|
||||||
|
defaultExpand={false}
|
||||||
|
actions={
|
||||||
|
<table className="table table--condensed table--fixed table--lbc-details">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td> {__('Original Publish Amount')}</td>
|
||||||
|
<td>
|
||||||
|
{claim && claim.amount ? (
|
||||||
|
<CreditAmount badge={false} amount={Number(claim.amount)} precision={2} />
|
||||||
|
) : (
|
||||||
|
<p>...</p>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{' '}
|
||||||
|
{__('Supports and Tips')}
|
||||||
|
<HelpLink href="https://lbry.com/faq/tipping" />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{claimIsMine && !pendingAmount && Boolean(supportsAmount) && (
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
button="link"
|
||||||
|
className="expandable__button"
|
||||||
|
icon={ICONS.UNLOCK}
|
||||||
|
label={<CreditAmount badge={false} amount={Number(supportsAmount)} precision={2} />}
|
||||||
|
onClick={() => {
|
||||||
|
openModal(MODALS.LIQUIDATE_SUPPORTS, { uri });
|
||||||
|
}}
|
||||||
|
/>{' '}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{(!claimIsMine || (claimIsMine && !pendingAmount && supportsAmount === 0)) && (
|
||||||
|
<CreditAmount badge={false} amount={Number(supportsAmount)} precision={2} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{claimIsMine && pendingAmount && <Spinner type={'small'} />}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div>
|
||||||
|
{__('Total Staked Amount')}
|
||||||
|
<HelpLink href="https://lbry.com/faq/tipping" />
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<CreditAmount badge={false} amount={Number(claim.meta.effective_amount)} precision={2} />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{__('Community Choice?')}
|
||||||
|
<HelpLink href="https://lbry.com/faq/naming" />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<Button
|
||||||
|
button="link"
|
||||||
|
label={claim.meta.is_controlling ? __('Yes') : __('No')}
|
||||||
|
navigate={`/$/${PAGES.TOP}?name=${claim.name}`}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FileValues;
|
|
@ -249,7 +249,6 @@ function PublishFile(props: Props) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
actionIconPadding={false}
|
|
||||||
icon={ICONS.PUBLISH}
|
icon={ICONS.PUBLISH}
|
||||||
disabled={disabled || balance === 0}
|
disabled={disabled || balance === 0}
|
||||||
title={
|
title={
|
||||||
|
|
33
ui/component/supportsLiquidate/index.js
Normal file
33
ui/component/supportsLiquidate/index.js
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import {
|
||||||
|
selectBalance,
|
||||||
|
selectTotalBalance,
|
||||||
|
selectClaimsBalance,
|
||||||
|
selectSupportsBalance,
|
||||||
|
selectTipsBalance,
|
||||||
|
makeSelectMetadataForUri,
|
||||||
|
makeSelectClaimForUri,
|
||||||
|
doSupportAbandonForClaim,
|
||||||
|
doFetchClaimListMine,
|
||||||
|
selectAbandonClaimSupportError,
|
||||||
|
} from 'lbry-redux';
|
||||||
|
import SupportsLiquidate from './view';
|
||||||
|
|
||||||
|
const select = (state, props) => ({
|
||||||
|
balance: selectBalance(state),
|
||||||
|
totalBalance: selectTotalBalance(state),
|
||||||
|
claimsBalance: selectClaimsBalance(state) || undefined,
|
||||||
|
supportsBalance: selectSupportsBalance(state) || undefined,
|
||||||
|
tipsBalance: selectTipsBalance(state) || undefined,
|
||||||
|
claim: makeSelectClaimForUri(props.uri)(state),
|
||||||
|
metadata: makeSelectMetadataForUri(props.uri)(state),
|
||||||
|
abandonClaimError: selectAbandonClaimSupportError(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
const perform = dispatch => ({
|
||||||
|
abandonSupportForClaim: (claimId, type, keep, preview) =>
|
||||||
|
dispatch(doSupportAbandonForClaim(claimId, type, keep, preview)),
|
||||||
|
fetchClaimListMine: () => dispatch(doFetchClaimListMine()),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(select, perform)(SupportsLiquidate);
|
167
ui/component/supportsLiquidate/view.jsx
Normal file
167
ui/component/supportsLiquidate/view.jsx
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
// @flow
|
||||||
|
import * as ICONS from 'constants/icons';
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import CreditAmount from 'component/common/credit-amount';
|
||||||
|
import Button from 'component/button';
|
||||||
|
import { Form, FormField } from 'component/common/form';
|
||||||
|
import Card from 'component/common/card';
|
||||||
|
import I18nMessage from 'component/i18nMessage';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
balance: number,
|
||||||
|
totalBalance: number,
|
||||||
|
claimsBalance: number,
|
||||||
|
supportsBalance: number,
|
||||||
|
tipsBalance: number,
|
||||||
|
claim: any,
|
||||||
|
metaData: any,
|
||||||
|
handleClose: () => void,
|
||||||
|
abandonSupportForClaim: (string, string, boolean | string, boolean) => any,
|
||||||
|
abandonClaimError: ?string,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SupportsLiquidate = (props: Props) => {
|
||||||
|
const { claim, abandonSupportForClaim, handleClose, abandonClaimError } = props;
|
||||||
|
const [previewBalance, setPreviewBalance] = useState(undefined);
|
||||||
|
const [amount, setAmount] = useState(0);
|
||||||
|
const [error, setError] = useState(false);
|
||||||
|
const initialMessage = '';
|
||||||
|
const [message, setMessage] = useState(initialMessage);
|
||||||
|
const keep =
|
||||||
|
previewBalance && amount && Number(amount) < previewBalance
|
||||||
|
? Number.parseFloat(String(previewBalance - Number(amount))).toFixed(8)
|
||||||
|
: false;
|
||||||
|
const claimId = claim && claim.claim_id;
|
||||||
|
const type = claim.value_type;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (claimId && abandonSupportForClaim) {
|
||||||
|
abandonSupportForClaim(claimId, type, false, true).then(r => {
|
||||||
|
setPreviewBalance(r.total_input);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [abandonSupportForClaim, claimId, type, setPreviewBalance]);
|
||||||
|
|
||||||
|
function handleSubmit() {
|
||||||
|
abandonSupportForClaim(claimId, type, keep, false).then(r => {
|
||||||
|
if (r) {
|
||||||
|
handleClose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleChange(a) {
|
||||||
|
if (a === undefined || isNaN(Number(a))) {
|
||||||
|
setMessage(__('Amount must be a number'));
|
||||||
|
setError(true);
|
||||||
|
setAmount('');
|
||||||
|
} else if (a === '') {
|
||||||
|
setAmount('');
|
||||||
|
setError(true);
|
||||||
|
setMessage(__('Amount cannot be blank'));
|
||||||
|
} else if (Number(a) > Number(previewBalance)) {
|
||||||
|
setMessage(__('Amount cannot be more than available'));
|
||||||
|
setError(false);
|
||||||
|
} else if (Number(a) === Number(previewBalance)) {
|
||||||
|
setMessage(__(`She's about to close up the library!`));
|
||||||
|
setAmount(a);
|
||||||
|
setError(false);
|
||||||
|
} else if (Number(a) > Number(previewBalance) / 2) {
|
||||||
|
setMessage(__('Your content will do better with more staked on it'));
|
||||||
|
setAmount(a);
|
||||||
|
setError(false);
|
||||||
|
} else if (a === '0') {
|
||||||
|
setMessage(__('Amount cannot be zero'));
|
||||||
|
setAmount(a);
|
||||||
|
setError(true);
|
||||||
|
} else {
|
||||||
|
setMessage(initialMessage);
|
||||||
|
setAmount(a);
|
||||||
|
setError(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
icon={ICONS.UNLOCK}
|
||||||
|
title={__('Unlock Tips')}
|
||||||
|
subtitle={
|
||||||
|
<>
|
||||||
|
<p>
|
||||||
|
{__('You can unlock all or some of this LBC at any time.')}{' '}
|
||||||
|
{__('Keeping it locked improves the trust and discoverability of your content.')}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<I18nMessage
|
||||||
|
tokens={{
|
||||||
|
learn_more: <Button button="link" label={__('Learn More')} href="https://lbry.com/faq/tipping" />,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
It's usually only worth unlocking what you intend to use immediately. %learn_more%
|
||||||
|
</I18nMessage>
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
body={
|
||||||
|
<>
|
||||||
|
<div className="section">
|
||||||
|
<I18nMessage
|
||||||
|
tokens={{
|
||||||
|
amount: (
|
||||||
|
<strong>
|
||||||
|
<CreditAmount badge={false} amount={Number(previewBalance)} precision={8} />
|
||||||
|
</strong>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
%amount% available to unlock
|
||||||
|
</I18nMessage>
|
||||||
|
</div>
|
||||||
|
<div className="section">
|
||||||
|
{previewBalance === 0 && <p>{__('No unlockable tips available')}</p>}
|
||||||
|
{previewBalance === undefined && <p>{__('Loading...')}</p>}
|
||||||
|
{previewBalance && (
|
||||||
|
<Form onSubmit={handleSubmit}>
|
||||||
|
<label htmlFor="supports_liquidate_range">{__('Amount to unlock')}</label>
|
||||||
|
<FormField
|
||||||
|
name="supports_liquidate_range"
|
||||||
|
type={'range'}
|
||||||
|
min={0}
|
||||||
|
step={0.01}
|
||||||
|
max={previewBalance} // times 100 to so we're more granular than whole numbers.
|
||||||
|
value={Number(amount) || previewBalance / 4} // by default, set it to 25% of available
|
||||||
|
onChange={e => handleChange(e.target.value)}
|
||||||
|
/>
|
||||||
|
<label className="range__label">
|
||||||
|
<span>0</span>
|
||||||
|
<span>{previewBalance / 2}</span>
|
||||||
|
<span>{previewBalance}</span>
|
||||||
|
</label>
|
||||||
|
<FormField
|
||||||
|
type="text"
|
||||||
|
value={amount || (previewBalance && previewBalance / 4)}
|
||||||
|
helper={message}
|
||||||
|
onChange={e => handleChange(e.target.value)}
|
||||||
|
/>
|
||||||
|
</Form>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
actions={
|
||||||
|
<React.Fragment>
|
||||||
|
{abandonClaimError ? (
|
||||||
|
<>
|
||||||
|
<div className="error-text">{__('%message%', { message: abandonClaimError })}</div>
|
||||||
|
<Button disabled={error} button="primary" onClick={handleClose} label={__('Done')} />
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Button disabled={error} button="primary" onClick={handleSubmit} label={__('Unlock')} />
|
||||||
|
)}
|
||||||
|
</React.Fragment>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SupportsLiquidate;
|
|
@ -79,7 +79,6 @@ export default function TagsSelect(props: Props) {
|
||||||
return (
|
return (
|
||||||
((showClose && !hasClosed) || !showClose) && (
|
((showClose && !hasClosed) || !showClose) && (
|
||||||
<Card
|
<Card
|
||||||
actionIconPadding={false}
|
|
||||||
icon={ICONS.TAG}
|
icon={ICONS.TAG}
|
||||||
title={
|
title={
|
||||||
hideHeader ? null : (
|
hideHeader ? null : (
|
||||||
|
|
|
@ -13,6 +13,7 @@ export const DOWNLOAD = 'Download';
|
||||||
export const PUBLISH = 'UploadCloud';
|
export const PUBLISH = 'UploadCloud';
|
||||||
export const REMOVE = 'X';
|
export const REMOVE = 'X';
|
||||||
export const ADD = 'Plus';
|
export const ADD = 'Plus';
|
||||||
|
export const SUBTRACT = 'Subtract';
|
||||||
export const EDIT = 'Edit';
|
export const EDIT = 'Edit';
|
||||||
export const DELETE = 'Trash';
|
export const DELETE = 'Trash';
|
||||||
export const REPORT = 'Flag';
|
export const REPORT = 'Flag';
|
||||||
|
|
|
@ -37,3 +37,4 @@ export const MOBILE_NAVIGATION = 'mobile_navigation';
|
||||||
export const SET_REFERRER = 'set_referrer';
|
export const SET_REFERRER = 'set_referrer';
|
||||||
export const REPOST = 'repost';
|
export const REPOST = 'repost';
|
||||||
export const SIGN_OUT = 'sign_out';
|
export const SIGN_OUT = 'sign_out';
|
||||||
|
export const LIQUIDATE_SUPPORTS = 'liquidate_supports';
|
||||||
|
|
|
@ -122,7 +122,6 @@ function ModalRepost(props: Props) {
|
||||||
return (
|
return (
|
||||||
<Modal isOpen type="card" onAborted={handleCloseModal} onConfirmed={handleCloseModal}>
|
<Modal isOpen type="card" onAborted={handleCloseModal} onConfirmed={handleCloseModal}>
|
||||||
<Card
|
<Card
|
||||||
actionIconPadding={false}
|
|
||||||
icon={ICONS.REPOST}
|
icon={ICONS.REPOST}
|
||||||
title={
|
title={
|
||||||
<span>
|
<span>
|
||||||
|
|
|
@ -36,6 +36,7 @@ import ModalMobileNavigation from 'modal/modalMobileNavigation';
|
||||||
import ModalSetReferrer from 'modal/modalSetReferrer';
|
import ModalSetReferrer from 'modal/modalSetReferrer';
|
||||||
import ModalRepost from 'modal/modalRepost';
|
import ModalRepost from 'modal/modalRepost';
|
||||||
import ModalSignOut from 'modal/modalSignOut';
|
import ModalSignOut from 'modal/modalSignOut';
|
||||||
|
import ModalLiquidateSupports from '../modalSupportsLiquidate';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
modal: { id: string, modalProps: {} },
|
modal: { id: string, modalProps: {} },
|
||||||
|
@ -131,6 +132,8 @@ function ModalRouter(props: Props) {
|
||||||
return <ModalRepost {...modalProps} />;
|
return <ModalRepost {...modalProps} />;
|
||||||
case MODALS.SIGN_OUT:
|
case MODALS.SIGN_OUT:
|
||||||
return <ModalSignOut {...modalProps} />;
|
return <ModalSignOut {...modalProps} />;
|
||||||
|
case MODALS.LIQUIDATE_SUPPORTS:
|
||||||
|
return <ModalLiquidateSupports {...modalProps} />;
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
15
ui/modal/modalSupportsLiquidate/index.js
Normal file
15
ui/modal/modalSupportsLiquidate/index.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { doHideModal } from 'redux/actions/app';
|
||||||
|
import { doAbandonClaim, selectTransactionItems } from 'lbry-redux';
|
||||||
|
import ModalSupportsLiquidate from './view';
|
||||||
|
|
||||||
|
const select = state => ({
|
||||||
|
transactionItems: selectTransactionItems(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
const perform = dispatch => ({
|
||||||
|
closeModal: () => dispatch(doHideModal()),
|
||||||
|
abandonClaim: (txid, nout) => dispatch(doAbandonClaim(txid, nout)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(select, perform)(ModalSupportsLiquidate);
|
19
ui/modal/modalSupportsLiquidate/view.jsx
Normal file
19
ui/modal/modalSupportsLiquidate/view.jsx
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
// @flow
|
||||||
|
import React from 'react';
|
||||||
|
import { Modal } from 'modal/modal';
|
||||||
|
import SupportsLiquidate from 'component/supportsLiquidate';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
closeModal: () => void,
|
||||||
|
uri: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function ModalSupportsLiquidate(props: Props) {
|
||||||
|
const { closeModal, uri } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal isOpen contentLabel={'Unlock Tips'} type="card" confirmButtonLabel="done" onAborted={closeModal}>
|
||||||
|
<SupportsLiquidate uri={uri} handleClose={closeModal} />
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
|
@ -10,6 +10,9 @@ import FileRenderInline from 'component/fileRenderInline';
|
||||||
import FileRenderDownload from 'component/fileRenderDownload';
|
import FileRenderDownload from 'component/fileRenderDownload';
|
||||||
import Card from 'component/common/card';
|
import Card from 'component/common/card';
|
||||||
import FileDetails from 'component/fileDetails';
|
import FileDetails from 'component/fileDetails';
|
||||||
|
import FileValues from 'component/fileValues';
|
||||||
|
import FileDescription from 'component/fileDescription';
|
||||||
|
|
||||||
import RecommendedContent from 'component/recommendedContent';
|
import RecommendedContent from 'component/recommendedContent';
|
||||||
import CommentsList from 'component/commentsList';
|
import CommentsList from 'component/commentsList';
|
||||||
import CommentCreate from 'component/commentCreate';
|
import CommentCreate from 'component/commentCreate';
|
||||||
|
@ -129,7 +132,12 @@ class FilePage extends React.Component<Props> {
|
||||||
</div>
|
</div>
|
||||||
<div className="section columns">
|
<div className="section columns">
|
||||||
<div className="card-stack">
|
<div className="card-stack">
|
||||||
|
<FileDescription uri={uri} />
|
||||||
|
|
||||||
|
<FileValues uri={uri} />
|
||||||
|
|
||||||
<FileDetails uri={uri} />
|
<FileDetails uri={uri} />
|
||||||
|
|
||||||
<Card
|
<Card
|
||||||
title={__('Leave a Comment')}
|
title={__('Leave a Comment')}
|
||||||
actions={
|
actions={
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
@import 'component/dat-gui';
|
@import 'component/dat-gui';
|
||||||
@import 'component/embed-player';
|
@import 'component/embed-player';
|
||||||
@import 'component/expandable';
|
@import 'component/expandable';
|
||||||
|
@import 'component/expanding-details';
|
||||||
@import 'component/file-properties';
|
@import 'component/file-properties';
|
||||||
@import 'component/file-render';
|
@import 'component/file-render';
|
||||||
@import 'component/form-field';
|
@import 'component/form-field';
|
||||||
|
|
|
@ -63,6 +63,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card__section--flex {
|
||||||
|
@extend .section__flex;
|
||||||
|
padding: var(--spacing-medium) var(--spacing-large);
|
||||||
|
}
|
||||||
|
|
||||||
.card__actions--inline {
|
.card__actions--inline {
|
||||||
@extend .card__actions;
|
@extend .card__actions;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
|
@ -138,6 +143,10 @@
|
||||||
.card__title--between {
|
.card__title--between {
|
||||||
@extend .card__title;
|
@extend .card__title;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
& > *:not(:last-child) {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.card__media--nsfw {
|
.card__media--nsfw {
|
||||||
|
@ -145,7 +154,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.card__header {
|
.card__header {
|
||||||
margin: var(--spacing-medium) var(--spacing-large);
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
|
|
||||||
|
@ -154,6 +163,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card__header--between {
|
||||||
|
@extend .card__header;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
.card__title {
|
.card__title {
|
||||||
font-size: var(--font-title);
|
font-size: var(--font-title);
|
||||||
font-weight: var(--font-weight-light);
|
font-weight: var(--font-weight-light);
|
||||||
|
|
|
@ -75,16 +75,27 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.table--file-details {
|
.table--details {
|
||||||
margin-top: var(--spacing-large);
|
|
||||||
font-size: var(--font-small);
|
font-size: var(--font-small);
|
||||||
|
}
|
||||||
|
.table--file-details {
|
||||||
|
@extend .table--details;
|
||||||
|
|
||||||
td:nth-of-type(1) {
|
td:nth-of-type(1) {
|
||||||
width: 30%;
|
width: 25%;
|
||||||
}
|
}
|
||||||
|
|
||||||
td:nth-of-type(2) {
|
td:nth-of-type(2) {
|
||||||
width: 70%;
|
width: 75%;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.table--lbc-details {
|
||||||
|
@extend .table--details;
|
||||||
|
|
||||||
|
td:nth-of-type(2) {
|
||||||
|
text-align: right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
15
ui/scss/component/expanding-details.scss
Normal file
15
ui/scss/component/expanding-details.scss
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
.expanding-details__header {
|
||||||
|
background-color: var(--color-card-background);
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
padding-left: var(--spacing-small);
|
||||||
|
}
|
||||||
|
|
||||||
|
.expanding-details {
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.expanding-details__body {
|
||||||
|
border-radius: var(--card-radius);
|
||||||
|
padding: 0 var(--spacing-small);
|
||||||
|
}
|
|
@ -6139,9 +6139,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#90ba18d0602956016ded63e816734713cfd2023a:
|
lbry-redux@lbryio/lbry-redux#1097a63d44a20b87e443fbaa48f95fe3ea5e3f70:
|
||||||
version "0.0.1"
|
version "0.0.1"
|
||||||
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/90ba18d0602956016ded63e816734713cfd2023a"
|
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/1097a63d44a20b87e443fbaa48f95fe3ea5e3f70"
|
||||||
dependencies:
|
dependencies:
|
||||||
proxy-polyfill "0.1.6"
|
proxy-polyfill "0.1.6"
|
||||||
reselect "^3.0.0"
|
reselect "^3.0.0"
|
||||||
|
|
Loading…
Reference in a new issue