Support markdown makeup in claim description #242

Closed
longle255 wants to merge 11 commits from claim-desc-markdown-support into master
10 changed files with 77 additions and 8 deletions

2
.gitignore vendored
View file

@ -17,3 +17,5 @@ dist
build/daemon.zip build/daemon.zip
.vimrc .vimrc
ui/yarn.lock

View file

@ -8,7 +8,10 @@ Web UI version numbers should always match the corresponding version of LBRY App
## [Unreleased] ## [Unreleased]
### Added ### Added
* Support markdown makeup in claim description
* State is persisted through app close and re-open, resulting in faster opens * State is persisted through app close and re-open, resulting in faster opens
*
### Changed ### Changed
* Upgraded to lbry daemon 0.13, including updating API signatures * Upgraded to lbry daemon 0.13, including updating API signatures

View file

@ -1,5 +1,7 @@
import React from "react"; import React from "react";
import ReactDOMServer from "react-dom/server";
import lbry from "../lbry.js"; import lbry from "../lbry.js";
import ReactMarkdown from "react-markdown";
//component/icon.js //component/icon.js
export class Icon extends React.PureComponent { export class Icon extends React.PureComponent {
@ -42,6 +44,40 @@ export class TruncatedText extends React.PureComponent {
} }
} }
export class TruncatedMarkdown extends React.PureComponent {
static propTypes = {
lines: React.PropTypes.number,
};
static defaultProps = {
lines: null,
};
transformMarkdown(text) {
// render markdown to html string then trim html tag
let htmlString = ReactDOMServer.renderToStaticMarkup(
<ReactMarkdown source={this.props.children} />
);
var txt = document.createElement("textarea");
txt.innerHTML = htmlString;
return txt.value.replace(/<(?:.|\n)*?>/gm, "");
}
render() {
let content = this.props.children && typeof this.props.children === "string"
? this.transformMarkdown(this.props.children)
: this.props.children;
return (
<span
className="truncated-text"
style={{ WebkitLineClamp: this.props.lines }}
>
{content}
</span>
);
}
}
export class BusyMessage extends React.PureComponent { export class BusyMessage extends React.PureComponent {
static propTypes = { static propTypes = {
message: React.PropTypes.string, message: React.PropTypes.string,

View file

@ -2,7 +2,12 @@ import React from "react";
import lbry from "lbry.js"; import lbry from "lbry.js";
import lbryuri from "lbryuri.js"; import lbryuri from "lbryuri.js";
import Link from "component/link"; import Link from "component/link";
import { Thumbnail, TruncatedText, Icon } from "component/common"; import {
Thumbnail,
TruncatedText,
Icon,
TruncatedMarkdown,
} from "component/common";
import FilePrice from "component/filePrice"; import FilePrice from "component/filePrice";
import UriIndicator from "component/uriIndicator"; import UriIndicator from "component/uriIndicator";
@ -86,7 +91,7 @@ class FileCard extends React.PureComponent {
style={{ backgroundImage: "url('" + metadata.thumbnail + "')" }} style={{ backgroundImage: "url('" + metadata.thumbnail + "')" }}
/>} />}
<div className="card__content card__subtext card__subtext--two-lines"> <div className="card__content card__subtext card__subtext--two-lines">
<TruncatedText lines={2}>{description}</TruncatedText> <TruncatedMarkdown lines={2}>{description}</TruncatedMarkdown>
</div> </div>
</Link> </Link>
{obscureNsfw && this.state.hovered {obscureNsfw && this.state.hovered

View file

@ -3,7 +3,11 @@ import lbry from "lbry.js";
import lbryuri from "lbryuri.js"; import lbryuri from "lbryuri.js";
import Link from "component/link"; import Link from "component/link";
import FileActions from "component/fileActions"; import FileActions from "component/fileActions";
import { Thumbnail, TruncatedText } from "component/common.js"; import {
Thumbnail,
TruncatedText,
TruncatedMarkdown,
} from "component/common.js";
import FilePrice from "component/filePrice"; import FilePrice from "component/filePrice";
import UriIndicator from "component/uriIndicator"; import UriIndicator from "component/uriIndicator";
@ -117,9 +121,9 @@ class FileTile extends React.PureComponent {
<h3><TruncatedText lines={1}>{title}</TruncatedText></h3> <h3><TruncatedText lines={1}>{title}</TruncatedText></h3>
</div> </div>
<div className="card__content card__subtext"> <div className="card__content card__subtext">
<TruncatedText lines={3}> <TruncatedMarkdown lines={3}>
{description} {description}
</TruncatedText> </TruncatedMarkdown>
</div> </div>
</div> </div>
</div> </div>

View file

@ -1,6 +1,8 @@
import React from "react"; import React from "react";
import FileSelector from "./file-selector.js"; import FileSelector from "./file-selector.js";
import { Icon } from "./common.js"; import { Icon } from "./common.js";
import SimpleMDE from "react-simplemde-editor";
import style from "react-simplemde-editor/dist/simplemde.min.css";
var formFieldCounter = 0, var formFieldCounter = 0,
formFieldFileSelectorTypes = ["file", "directory"], formFieldFileSelectorTypes = ["file", "directory"],
@ -38,6 +40,9 @@ export class FormField extends React.PureComponent {
} else if (this.props.type == "text-number") { } else if (this.props.type == "text-number") {
this._element = "input"; this._element = "input";
this._type = "text"; this._type = "text";
} else if (this.props.type == "SimpleMDE") {
this._element = SimpleMDE;
this._type = "textarea";
} else if (formFieldFileSelectorTypes.includes(this.props.type)) { } else if (formFieldFileSelectorTypes.includes(this.props.type)) {
this._element = "input"; this._element = "input";
this._type = "hidden"; this._type = "hidden";
@ -81,6 +86,8 @@ export class FormField extends React.PureComponent {
getValue() { getValue() {
if (this.props.type == "checkbox") { if (this.props.type == "checkbox") {
return this.refs.field.checked; return this.refs.field.checked;
} else if (this.props.type == "SimpleMDE") {
return this.refs.field.simplemde.value();
} else { } else {
return this.refs.field.value; return this.refs.field.value;
} }
@ -106,7 +113,6 @@ export class FormField extends React.PureComponent {
delete otherProps.className; delete otherProps.className;
delete otherProps.postfix; delete otherProps.postfix;
delete otherProps.prefix; delete otherProps.prefix;
const element = ( const element = (
<this._element <this._element
id={elementId} id={elementId}

View file

@ -1,4 +1,5 @@
import React from "react"; import React from "react";
import ReactMarkdown from "react-markdown";
import lbry from "lbry.js"; import lbry from "lbry.js";
import lbryuri from "lbryuri.js"; import lbryuri from "lbryuri.js";
import Video from "component/video"; import Video from "component/video";
@ -118,7 +119,11 @@ class FilePage extends React.PureComponent {
</div> </div>
</div> </div>
<div className="card__content card__subtext card__subtext card__subtext--allow-newlines"> <div className="card__content card__subtext card__subtext card__subtext--allow-newlines">
{metadata && metadata.description} <ReactMarkdown
source={(metadata && metadata.description) || ""}
escapeHtml={true}
disallowedTypes={["Heading", "HtmlInline", "HtmlBlock"]}
/>
</div> </div>
</div> </div>
{metadata {metadata

View file

@ -520,7 +520,7 @@ class PublishPage extends React.PureComponent {
<div className="card__content"> <div className="card__content">
<FormRow <FormRow
label={__("Description")} label={__("Description")}
type="textarea" type="SimpleMDE"
ref="meta_description" ref="meta_description"
name="description" name="description"
placeholder={__("Description of your content")} placeholder={__("Description of your content")}

View file

@ -31,8 +31,10 @@
"rc-progress": "^2.0.6", "rc-progress": "^2.0.6",
"react": "^15.4.0", "react": "^15.4.0",
"react-dom": "^15.4.0", "react-dom": "^15.4.0",
"react-markdown": "^2.5.0",
"react-modal": "^1.5.2", "react-modal": "^1.5.2",
"react-redux": "^5.0.3", "react-redux": "^5.0.3",
"react-simplemde-editor": "^3.6.11",
"redux": "^3.6.0", "redux": "^3.6.0",
"redux-action-buffer": "^1.1.0", "redux-action-buffer": "^1.1.0",
"redux-logger": "^3.0.1", "redux-logger": "^3.0.1",
@ -53,6 +55,7 @@
"babel-preset-es2015": "^6.18.0", "babel-preset-es2015": "^6.18.0",
"babel-preset-react": "^6.16.0", "babel-preset-react": "^6.16.0",
"babel-preset-stage-2": "^6.18.0", "babel-preset-stage-2": "^6.18.0",
"css-loader": "^0.28.4",
"eslint": "^3.10.2", "eslint": "^3.10.2",
"eslint-config-airbnb": "^13.0.0", "eslint-config-airbnb": "^13.0.0",
"eslint-loader": "^1.6.1", "eslint-loader": "^1.6.1",
@ -64,6 +67,7 @@
"lint-staged": "^3.6.0", "lint-staged": "^3.6.0",
"node-sass": "^3.13.0", "node-sass": "^3.13.0",
"prettier": "^1.4.2", "prettier": "^1.4.2",
"style-loader": "^0.18.2",
"webpack": "^2.6.1", "webpack": "^2.6.1",
"webpack-dev-server": "^2.4.4", "webpack-dev-server": "^2.4.4",
"webpack-notifier": "^1.5.0", "webpack-notifier": "^1.5.0",

View file

@ -163,4 +163,8 @@ input[type="text"].input-copyable {
} }
.form-field__helper { .form-field__helper {
color: $color-help; color: $color-help;
}
.form-field__input.form-field__input-SimpleMDE .CodeMirror-scroll {
height: auto;
} }