Support markdown makeup in claim description #242
10 changed files with 77 additions and 8 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -17,3 +17,5 @@ dist
|
||||||
|
|
||||||
build/daemon.zip
|
build/daemon.zip
|
||||||
.vimrc
|
.vimrc
|
||||||
|
|
||||||
|
ui/yarn.lock
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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")}
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -164,3 +164,7 @@ 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;
|
||||||
|
}
|
Loading…
Reference in a new issue