From 284ab8a01af4bc802e8d9fe5ab405582d2d6f4b7 Mon Sep 17 00:00:00 2001
From: 6ea86b96 <6ea86b96@gmail.com>
Date: Mon, 10 Jul 2017 21:49:12 +0700
Subject: [PATCH 01/26] Move fetching my channels into redux
---
ui/js/actions/content.js | 17 ++++++++++
ui/js/constants/action_types.js | 4 +++
ui/js/page/publish/index.js | 11 ++++--
ui/js/page/publish/view.jsx | 59 +++++++++++++++++----------------
ui/js/reducers/claims.js | 35 ++++++++++---------
ui/js/selectors/claims.js | 18 ++++++++++
6 files changed, 99 insertions(+), 45 deletions(-)
diff --git a/ui/js/actions/content.js b/ui/js/actions/content.js
index b80f8fc0c..d9566b091 100644
--- a/ui/js/actions/content.js
+++ b/ui/js/actions/content.js
@@ -339,3 +339,20 @@ export function doFetchClaimListMine() {
});
};
}
+
+export function doFetchChannelListMine() {
+ return function(dispatch, getState) {
+ dispatch({
+ type: types.FETCH_CHANNEL_LIST_MINE_STARTED,
+ });
+
+ const callback = channels => {
+ dispatch({
+ type: types.FETCH_CHANNEL_LIST_MINE_COMPLETED,
+ data: { claims: channels },
+ });
+ };
+
+ lbry.channel_list_mine().then(callback);
+ };
+}
diff --git a/ui/js/constants/action_types.js b/ui/js/constants/action_types.js
index 216c84762..e278bac3d 100644
--- a/ui/js/constants/action_types.js
+++ b/ui/js/constants/action_types.js
@@ -64,6 +64,10 @@ export const FETCH_AVAILABILITY_COMPLETED = "FETCH_AVAILABILITY_COMPLETED";
export const FILE_DELETE = "FILE_DELETE";
export const ABANDON_CLAIM_STARTED = "ABANDON_CLAIM_STARTED";
export const ABANDON_CLAIM_COMPLETED = "ABANDON_CLAIM_COMPLETED";
+export const FETCH_CHANNEL_LIST_MINE_STARTED =
+ "FETCH_CHANNEL_LIST_MINE_STARTED";
+export const FETCH_CHANNEL_LIST_MINE_COMPLETED =
+ "FETCH_CHANNEL_LIST_MINE_COMPLETED";
// Search
export const SEARCH_STARTED = "SEARCH_STARTED";
diff --git a/ui/js/page/publish/index.js b/ui/js/page/publish/index.js
index fac4419c1..bfe1f06e0 100644
--- a/ui/js/page/publish/index.js
+++ b/ui/js/page/publish/index.js
@@ -2,13 +2,19 @@ import React from "react";
import { connect } from "react-redux";
import { doNavigate, doHistoryBack } from "actions/app";
import { doClaimRewardType } from "actions/rewards";
-import { selectMyClaims } from "selectors/claims";
-import { doFetchClaimListMine } from "actions/content";
+import {
+ selectMyClaims,
+ selectFetchingMyChannels,
+ selectMyChannelClaims,
+} from "selectors/claims";
+import { doFetchClaimListMine, doFetchChannelListMine } from "actions/content";
import rewards from "rewards";
import PublishPage from "./view";
const select = state => ({
myClaims: selectMyClaims(state),
+ fetchingChannels: selectFetchingMyChannels(state),
+ channels: selectMyChannelClaims(state),
});
const perform = dispatch => ({
@@ -17,6 +23,7 @@ const perform = dispatch => ({
fetchClaimListMine: () => dispatch(doFetchClaimListMine()),
claimFirstChannelReward: () =>
dispatch(doClaimRewardType(rewards.TYPE_FIRST_CHANNEL)),
+ fetchChannelListMine: () => dispatch(doFetchChannelListMine()),
});
export default connect(select, perform)(PublishPage);
diff --git a/ui/js/page/publish/view.jsx b/ui/js/page/publish/view.jsx
index 0693d61cf..cb9e938c8 100644
--- a/ui/js/page/publish/view.jsx
+++ b/ui/js/page/publish/view.jsx
@@ -5,6 +5,7 @@ import { FormField, FormRow } from "component/form.js";
import Link from "component/link";
import rewards from "rewards";
import Modal from "component/modal";
+import { BusyMessage } from "component/common";
class PublishPage extends React.PureComponent {
constructor(props) {
@@ -13,7 +14,6 @@ class PublishPage extends React.PureComponent {
this._requiredFields = ["meta_title", "name", "bid", "tos_agree"];
this.state = {
- channels: null,
rawName: "",
name: "",
bid: 10,
@@ -41,15 +41,18 @@ class PublishPage extends React.PureComponent {
}
_updateChannelList(channel) {
+ const { fetchingChannels, fetchChannelListMine } = this.props;
+
+ if (!fetchingChannels) fetchChannelListMine();
// Calls API to update displayed list of channels. If a channel name is provided, will select
// that channel at the same time (used immediately after creating a channel)
- lbry.channel_list_mine().then(channels => {
- this.props.claimFirstChannelReward();
- this.setState({
- channels: channels,
- ...(channel ? { channel } : {}),
- });
- });
+ // lbry.channel_list_mine().then(channels => {
+ // this.props.claimFirstChannelReward();
+ // this.setState({
+ // channels: channels,
+ // ...(channel ? { channel } : {}),
+ // });
+ // });
}
handleSubmit(event) {
@@ -465,10 +468,6 @@ class PublishPage extends React.PureComponent {
}
render() {
- if (this.state.channels === null) {
- return null;
- }
-
const lbcInputHelp = __(
"This LBC remains yours and the deposit can be undone at any time."
);
@@ -729,22 +728,26 @@ class PublishPage extends React.PureComponent {
- {
- this.handleChannelChange(event);
- }}
- value={this.state.channel}
- >
-
- {__("Anonymous")}
-
- {this.state.channels.map(({ name }) =>
- {name}
- )}
- {__("New identity...")}
-
+ {this.props.fetchingChannels
+ ?
+ : {
+ this.handleChannelChange(event);
+ }}
+ value={this.state.channel}
+ >
+
+ {__("Anonymous")}
+
+ {this.props.channels.map(({ name }) =>
+ {name}
+ )}
+
+ {__("New identity...")}
+
+ }
{this.state.channel == "new"
?
diff --git a/ui/js/reducers/claims.js b/ui/js/reducers/claims.js
index 7417bc2fb..f71e2cadd 100644
--- a/ui/js/reducers/claims.js
+++ b/ui/js/reducers/claims.js
@@ -50,21 +50,26 @@ reducers[types.FETCH_CLAIM_LIST_MINE_COMPLETED] = function(state, action) {
});
};
-// reducers[types.FETCH_CHANNEL_CLAIMS_STARTED] = function(state, action) {
-// const {
-// uri,
-// } = action.data
-//
-// const newClaims = Object.assign({}, state.claimsByChannel)
-//
-// if (claims !== undefined) {
-// newClaims[uri] = claims
-// }
-//
-// return Object.assign({}, state, {
-// claimsByChannel: newClaims
-// })
-// }
+reducers[types.FETCH_CHANNEL_LIST_MINE_STARTED] = function(state, action) {
+ return Object.assign({}, state, { fetchingMyChannels: true });
+};
+
+reducers[types.FETCH_CHANNEL_LIST_MINE_COMPLETED] = function(state, action) {
+ const { claims } = action.data;
+ const myChannelClaims = new Set(state.myChannelClaims);
+ const byId = Object.assign({}, state.byId);
+
+ claims.forEach(claim => {
+ myChannelClaims.add(claim.claim_id);
+ byId[claims.claim_id] = claim;
+ });
+
+ return Object.assign({}, state, {
+ byId,
+ fetchingMyChannels: false,
+ myChannelClaims,
+ });
+};
reducers[types.FETCH_CHANNEL_CLAIMS_COMPLETED] = function(state, action) {
const { uri, claims } = action.data;
diff --git a/ui/js/selectors/claims.js b/ui/js/selectors/claims.js
index b966375e9..1792db453 100644
--- a/ui/js/selectors/claims.js
+++ b/ui/js/selectors/claims.js
@@ -124,3 +124,21 @@ export const selectMyClaimsOutpoints = createSelector(
return outpoints;
}
);
+
+export const selectFetchingMyChannels = createSelector(
+ _selectState,
+ state => !!state.fetchingMyChannels
+);
+
+export const selectMyChannelClaims = createSelector(
+ _selectState,
+ selectClaimsById,
+ (state, byId) => {
+ const ids = state.myChannelClaims || [];
+ const claims = [];
+
+ ids.forEach(id => claims.push(byId[id]));
+
+ return claims;
+ }
+);
From e01868d29b80e8394f29c1acfa4dda0d3b264401 Mon Sep 17 00:00:00 2001
From: 6ea86b96 <6ea86b96@gmail.com>
Date: Mon, 12 Jun 2017 15:01:22 +0700
Subject: [PATCH 02/26] Move claim lookup on publish page into redux
---
ui/js/page/publish/index.js | 11 ++-
ui/js/page/publish/view.jsx | 133 ++++++++++++++++--------------------
ui/js/selectors/claims.js | 26 +++++--
3 files changed, 88 insertions(+), 82 deletions(-)
diff --git a/ui/js/page/publish/index.js b/ui/js/page/publish/index.js
index bfe1f06e0..d959b04bd 100644
--- a/ui/js/page/publish/index.js
+++ b/ui/js/page/publish/index.js
@@ -6,8 +6,14 @@ import {
selectMyClaims,
selectFetchingMyChannels,
selectMyChannelClaims,
+ selectClaimsByUri,
} from "selectors/claims";
-import { doFetchClaimListMine, doFetchChannelListMine } from "actions/content";
+import { selectResolvingUris } from "selectors/content";
+import {
+ doFetchClaimListMine,
+ doFetchChannelListMine,
+ doResolveUri,
+} from "actions/content";
import rewards from "rewards";
import PublishPage from "./view";
@@ -15,6 +21,8 @@ const select = state => ({
myClaims: selectMyClaims(state),
fetchingChannels: selectFetchingMyChannels(state),
channels: selectMyChannelClaims(state),
+ claimsByUri: selectClaimsByUri(state),
+ resolvingUris: selectResolvingUris(state),
});
const perform = dispatch => ({
@@ -24,6 +32,7 @@ const perform = dispatch => ({
claimFirstChannelReward: () =>
dispatch(doClaimRewardType(rewards.TYPE_FIRST_CHANNEL)),
fetchChannelListMine: () => dispatch(doFetchChannelListMine()),
+ resolveUri: uri => dispatch(doResolveUri(uri)),
});
export default connect(select, perform)(PublishPage);
diff --git a/ui/js/page/publish/view.jsx b/ui/js/page/publish/view.jsx
index cb9e938c8..a76d4eb71 100644
--- a/ui/js/page/publish/view.jsx
+++ b/ui/js/page/publish/view.jsx
@@ -23,9 +23,6 @@ class PublishPage extends React.PureComponent {
channel: "anonymous",
newChannelName: "@",
newChannelBid: 10,
- nameResolved: null,
- myClaimExists: null,
- topClaimValue: 0.0,
myClaimValue: 0.0,
myClaimMetadata: null,
copyrightNotice: "",
@@ -44,15 +41,6 @@ class PublishPage extends React.PureComponent {
const { fetchingChannels, fetchChannelListMine } = this.props;
if (!fetchingChannels) fetchChannelListMine();
- // Calls API to update displayed list of channels. If a channel name is provided, will select
- // that channel at the same time (used immediately after creating a channel)
- // lbry.channel_list_mine().then(channels => {
- // this.props.claimFirstChannelReward();
- // this.setState({
- // channels: channels,
- // ...(channel ? { channel } : {}),
- // });
- // });
}
handleSubmit(event) {
@@ -65,7 +53,7 @@ class PublishPage extends React.PureComponent {
});
let checkFields = this._requiredFields;
- if (!this.state.myClaimExists) {
+ if (!this.myClaimExists()) {
checkFields.unshift("file");
}
@@ -182,6 +170,49 @@ class PublishPage extends React.PureComponent {
});
}
+ claim() {
+ const { claimsByUri } = this.props;
+ const { uri } = this.state;
+
+ return claimsByUri[uri];
+ }
+
+ topClaimValue() {
+ if (!this.claim()) return null;
+
+ return parseFloat(this.claim().amount);
+ }
+
+ myClaimExists() {
+ const { myClaims } = this.props;
+ const { name } = this.state;
+
+ if (!name) return false;
+
+ return !!myClaims.find(claim => claim.name === name);
+ }
+
+ topClaimIsMine() {
+ const myClaimInfo = this.myClaimInfo();
+ const { claimsByUri } = this.props;
+ const { uri } = this.state;
+
+ if (!uri) return null;
+
+ const claim = claimsByUri[uri];
+
+ if (!claim) return true;
+ if (!myClaimInfo) return false;
+
+ return myClaimInfo.amount >= claimInfo.amount;
+ }
+
+ myClaimInfo() {
+ return Object.values(this.props.myClaims).find(
+ claim => claim.name === name
+ );
+ }
+
handleNameChange(event) {
var rawName = event.target.value;
@@ -189,7 +220,7 @@ class PublishPage extends React.PureComponent {
this.setState({
rawName: "",
name: "",
- nameResolved: false,
+ uri: "",
});
return;
@@ -203,61 +234,14 @@ class PublishPage extends React.PureComponent {
}
const name = rawName.toLowerCase();
+ const uri = lbryuri.normalize(name);
this.setState({
rawName: rawName,
name: name,
- nameResolved: null,
- myClaimExists: null,
+ uri,
});
- const myClaimInfo = Object.values(this.props.myClaims).find(
- claim => claim.name === name
- );
-
- this.setState({
- myClaimExists: !!myClaimInfo,
- });
- lbry.resolve({ uri: name }).then(
- claimInfo => {
- if (name != this.state.name) {
- return;
- }
-
- if (!claimInfo) {
- this.setState({
- nameResolved: false,
- });
- } else {
- const topClaimIsMine =
- myClaimInfo && myClaimInfo.amount >= claimInfo.amount;
- const newState = {
- nameResolved: true,
- topClaimValue: parseFloat(claimInfo.amount),
- myClaimExists: !!myClaimInfo,
- myClaimValue: myClaimInfo ? parseFloat(myClaimInfo.amount) : null,
- myClaimMetadata: myClaimInfo ? myClaimInfo.value : null,
- topClaimIsMine: topClaimIsMine,
- };
-
- if (topClaimIsMine) {
- newState.bid = myClaimInfo.amount;
- } else if (this.state.myClaimMetadata) {
- // Just changed away from a name we have a claim on, so clear pre-fill
- newState.bid = "";
- }
-
- this.setState(newState);
- }
- },
- () => {
- // Assume an error means the name is available
- this.setState({
- name: name,
- nameResolved: false,
- myClaimExists: false,
- });
- }
- );
+ this.props.resolveUri(uri);
}
handleBidChange(event) {
@@ -427,11 +411,16 @@ class PublishPage extends React.PureComponent {
}
getNameBidHelpText() {
- if (!this.state.name) {
+ if (
+ this.state.uri &&
+ this.props.resolvingUris.indexOf(this.state.uri) !== -1
+ ) {
+ return ;
+ } else if (!this.state.name) {
return __("Select a URL for this publish.");
- } else if (this.state.nameResolved === false) {
+ } else if (!this.claim()) {
return __("This URL is unused.");
- } else if (this.state.myClaimExists) {
+ } else if (this.myClaimExists()) {
return __(
"You have already used this URL. Publishing to it again will update your previous publish."
);
@@ -496,7 +485,7 @@ class PublishPage extends React.PureComponent {
this.onFileChange(event);
}}
helper={
- this.state.myClaimExists
+ this.myClaimExists()
? __(
"If you don't choose a file, the file from your existing claim will be used."
)
@@ -829,11 +818,7 @@ class PublishPage extends React.PureComponent {
this.handleBidChange(event);
}}
value={this.state.bid}
- placeholder={
- this.state.nameResolved
- ? this.state.topClaimValue + 10
- : 100
- }
+ placeholder={this.claim() ? this.topClaimValue() + 10 : 100}
helper={lbcInputHelp}
/>
@@ -898,7 +883,7 @@ class PublishPage extends React.PureComponent {
>
{__("Your file has been published to LBRY at the address")}
- {" "}lbry://{this.state.name}
!
+ {" "}{this.state.uri}
!
{__(
diff --git a/ui/js/selectors/claims.js b/ui/js/selectors/claims.js
index 1792db453..3478c9fc4 100644
--- a/ui/js/selectors/claims.js
+++ b/ui/js/selectors/claims.js
@@ -105,21 +105,33 @@ export const selectClaimListMineIsPending = createSelector(
state => state.isClaimListMinePending
);
-export const selectMyClaims = createSelector(
+export const selectMyClaimsRaw = createSelector(
_selectState,
state => new Set(state.myClaims)
);
+export const selectMyClaims = createSelector(
+ selectMyClaimsRaw,
+ selectClaimsById,
+ (myClaimIds, byId) => {
+ const claims = [];
+
+ myClaimIds.forEach(id => {
+ const claim = byId[id];
+
+ if (claim) claims.push(claim);
+ });
+
+ return claims;
+ }
+);
+
export const selectMyClaimsOutpoints = createSelector(
selectMyClaims,
- selectClaimsById,
- (claimIds, byId) => {
+ myClaims => {
const outpoints = [];
- claimIds.forEach(claimId => {
- const claim = byId[claimId];
- if (claim) outpoints.push(`${claim.txid}:${claim.nout}`);
- });
+ myClaims.forEach(claim => outpoints.push(`${claim.txid}:${claim.nout}`));
return outpoints;
}
From df954882bc9484b85eb0fa4033918281669b0371 Mon Sep 17 00:00:00 2001
From: 6ea86b96 <6ea86b96@gmail.com>
Date: Fri, 16 Jun 2017 10:06:01 +0700
Subject: [PATCH 03/26] Cache channel claims
---
ui/js/store.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/ui/js/store.js b/ui/js/store.js
index 0ec06c017..1bb5ec058 100644
--- a/ui/js/store.js
+++ b/ui/js/store.js
@@ -90,6 +90,7 @@ const saveClaimsFilter = createFilter("claims", [
"byId",
"claimsByUri",
"myClaims",
+ "myChannelClaims",
]);
const persistOptions = {
From 8325828f6e18d63fd3cdb04924a196ab983ed066 Mon Sep 17 00:00:00 2001
From: 6ea86b96 <6ea86b96@gmail.com>
Date: Sun, 18 Jun 2017 00:59:18 +0700
Subject: [PATCH 04/26] Progress towards working publish
---
ui/js/actions/content.js | 90 +++
ui/js/component/common.js | 34 ++
ui/js/constants/action_types.js | 5 +
ui/js/lbryuri.js | 2 +
ui/js/page/publish/index.js | 4 +
ui/js/page/publish/view.jsx | 344 ++++++++----
ui/js/reducers/claims.js | 36 +-
ui/js/reducers/file_info.js | 59 +-
ui/js/selectors/file_info.js | 19 +-
ui/js/store.js | 8 +-
ui/yarn.lock | 957 +++++++++++++++++++++++++++++++-
11 files changed, 1399 insertions(+), 159 deletions(-)
diff --git a/ui/js/actions/content.js b/ui/js/actions/content.js
index d9566b091..ca6f1850d 100644
--- a/ui/js/actions/content.js
+++ b/ui/js/actions/content.js
@@ -15,6 +15,7 @@ import { selectBadgeNumber } from "selectors/app";
import { selectTotalDownloadProgress } from "selectors/file_info";
import setBadge from "util/setBadge";
import setProgressBar from "util/setProgressBar";
+import { doFileList } from "actions/file_info";
import batchActions from "util/batchActions";
const { ipcRenderer } = require("electron");
@@ -356,3 +357,92 @@ export function doFetchChannelListMine() {
lbry.channel_list_mine().then(callback);
};
}
+
+export function doCreateChannel(name, amount) {
+ return function(dispatch, getState) {
+ dispatch({
+ type: types.CREATE_CHANNEL_STARTED,
+ });
+
+ return new Promise((resolve, reject) => {
+ lbry
+ .channel_new({
+ channel_name: name,
+ amount: parseFloat(amount),
+ })
+ .then(
+ channelClaim => {
+ channelClaim.name = name;
+ dispatch({
+ type: types.CREATE_CHANNEL_COMPLETED,
+ data: { channelClaim },
+ });
+ resolve(channelClaim);
+ },
+ err => {
+ resolve(err);
+ }
+ );
+ });
+ };
+}
+
+export function doPublish(params) {
+ return function(dispatch, getState) {
+ let uri;
+ const { name, channel_name } = params;
+ if (channel_name) {
+ uri = lbryuri.build({ name: channel_name, path: name }, false);
+ } else {
+ uri = lbryuri.build({ name: name }, false);
+ }
+ const pendingPublish = {
+ name,
+ channel_name,
+ claim_id: "pending_claim_" + uri,
+ txid: "pending_" + uri,
+ nout: 0,
+ outpoint: "pending_" + uri + ":0",
+ time: Date.now(),
+ };
+
+ dispatch({
+ type: types.PUBLISH_STARTED,
+ data: {
+ params,
+ pendingPublish,
+ },
+ });
+
+ return new Promise((resolve, reject) => {
+ const success = claim => {
+ claim.name = params.name;
+ claim.channel_name = params.channel_name;
+ dispatch({
+ type: types.PUBLISH_COMPLETED,
+ data: {
+ claim,
+ uri,
+ pendingPublish,
+ },
+ });
+ dispatch(doFileList());
+ resolve(claim);
+ };
+ const failure = error => {
+ dispatch({
+ type: types.PUBLISH_FAILED,
+ data: {
+ error,
+ params,
+ uri,
+ pendingPublish,
+ },
+ });
+ reject(error);
+ };
+
+ lbry.publish(params).then(success, failure);
+ });
+ };
+}
diff --git a/ui/js/component/common.js b/ui/js/component/common.js
index 38dbf83fd..1b48bc4df 100644
--- a/ui/js/component/common.js
+++ b/ui/js/component/common.js
@@ -42,6 +42,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(
+
+ );
+ 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 (
+
+ {content}
+
+ );
+ }
+}
+
export class BusyMessage extends React.PureComponent {
static propTypes = {
message: React.PropTypes.string,
diff --git a/ui/js/constants/action_types.js b/ui/js/constants/action_types.js
index e278bac3d..457761441 100644
--- a/ui/js/constants/action_types.js
+++ b/ui/js/constants/action_types.js
@@ -68,6 +68,11 @@ export const FETCH_CHANNEL_LIST_MINE_STARTED =
"FETCH_CHANNEL_LIST_MINE_STARTED";
export const FETCH_CHANNEL_LIST_MINE_COMPLETED =
"FETCH_CHANNEL_LIST_MINE_COMPLETED";
+export const CREATE_CHANNEL_STARTED = "CREATE_CHANNEL_STARTED";
+export const CREATE_CHANNEL_COMPLETED = "CREATE_CHANNEL_COMPLETED";
+export const PUBLISH_STARTED = "PUBLISH_STARTED";
+export const PUBLISH_COMPLETED = "PUBLISH_COMPLETED";
+export const PUBLISH_FAILED = "PUBLISH_FAILED";
// Search
export const SEARCH_STARTED = "SEARCH_STARTED";
diff --git a/ui/js/lbryuri.js b/ui/js/lbryuri.js
index 1fcfdec58..42a825949 100644
--- a/ui/js/lbryuri.js
+++ b/ui/js/lbryuri.js
@@ -203,6 +203,8 @@ lbryuri.build = function(uriObj, includeProto = true, allowExtraProps = false) {
/* Takes a parseable LBRY URI and converts it to standard, canonical format (currently this just
* consists of adding the lbry:// prefix if needed) */
lbryuri.normalize = function(uri) {
+ if (uri.match(/pending_claim/)) return uri;
+
const { name, path, bidPosition, claimSequence, claimId } = lbryuri.parse(
uri
);
diff --git a/ui/js/page/publish/index.js b/ui/js/page/publish/index.js
index d959b04bd..f296f1687 100644
--- a/ui/js/page/publish/index.js
+++ b/ui/js/page/publish/index.js
@@ -13,6 +13,8 @@ import {
doFetchClaimListMine,
doFetchChannelListMine,
doResolveUri,
+ doCreateChannel,
+ doPublish,
} from "actions/content";
import rewards from "rewards";
import PublishPage from "./view";
@@ -33,6 +35,8 @@ const perform = dispatch => ({
dispatch(doClaimRewardType(rewards.TYPE_FIRST_CHANNEL)),
fetchChannelListMine: () => dispatch(doFetchChannelListMine()),
resolveUri: uri => dispatch(doResolveUri(uri)),
+ createChannel: (name, amount) => dispatch(doCreateChannel(name, amount)),
+ publish: params => dispatch(doPublish(params)),
});
export default connect(select, perform)(PublishPage);
diff --git a/ui/js/page/publish/view.jsx b/ui/js/page/publish/view.jsx
index a76d4eb71..12b38cbd7 100644
--- a/ui/js/page/publish/view.jsx
+++ b/ui/js/page/publish/view.jsx
@@ -125,16 +125,11 @@ class PublishPage extends React.PureComponent {
publishArgs.file_path = this.refs.file.getValue();
}
- lbry.publishDeprecated(
- publishArgs,
- message => {
- this.handlePublishStarted();
- },
- null,
- error => {
- this.handlePublishError(error);
- }
- );
+ const success = claim => {};
+ const failure = error => this.handlePublishError(error);
+
+ this.handlePublishStarted();
+ this.props.publish(publishArgs).then(success, failure);
};
if (this.state.isFee) {
@@ -216,6 +211,10 @@ class PublishPage extends React.PureComponent {
handleNameChange(event) {
var rawName = event.target.value;
+ this.nameChanged(rawName);
+ }
+
+ nameChanged(rawName) {
if (!rawName) {
this.setState({
rawName: "",
@@ -233,15 +232,26 @@ class PublishPage extends React.PureComponent {
return;
}
+ let channel = "";
+ if (this.state.channel !== "anonymous") channel = this.state.channel;
+
const name = rawName.toLowerCase();
- const uri = lbryuri.normalize(name);
+ const uri = lbryuri.build({ contentName: name, channelName: channel });
this.setState({
rawName: rawName,
name: name,
uri,
});
- this.props.resolveUri(uri);
+ if (this.resolveUriTimeout) {
+ clearTimeout(this.resolveUriTimeout);
+ this.resolveUriTimeout = undefined;
+ }
+ const resolve = () => this.props.resolveUri(uri);
+
+ this.resolveUriTimeout = setTimeout(resolve.bind(this), 500, {
+ once: true,
+ });
}
handleBidChange(event) {
@@ -302,40 +312,12 @@ class PublishPage extends React.PureComponent {
});
}
- handleChannelChange(event) {
- const channel = event.target.value;
-
+ handleChannelChange(channelName) {
this.setState({
- channel: channel,
- });
- }
-
- handleNewChannelNameChange(event) {
- const newChannelName = event.target.value.startsWith("@")
- ? event.target.value
- : "@" + event.target.value;
-
- if (
- newChannelName.length > 1 &&
- !lbryuri.isValidName(newChannelName.substr(1), false)
- ) {
- this.refs.newChannelName.showError(
- __("LBRY channel names must contain only letters, numbers and dashes.")
- );
- return;
- } else {
- this.refs.newChannelName.clearError();
- }
-
- this.setState({
- newChannelName: newChannelName,
- });
- }
-
- handleNewChannelBidChange(event) {
- this.setState({
- newChannelBid: event.target.value,
+ channel: channelName,
});
+ const nameChanged = () => this.nameChanged(this.state.rawName);
+ setTimeout(nameChanged.bind(this), 500, { once: true });
}
handleTOSChange(event) {
@@ -413,19 +395,26 @@ class PublishPage extends React.PureComponent {
getNameBidHelpText() {
if (
this.state.uri &&
- this.props.resolvingUris.indexOf(this.state.uri) !== -1
+ this.props.resolvingUris.indexOf(this.state.uri) !== -1 &&
+ this.claim() === undefined
) {
return ;
} else if (!this.state.name) {
return __("Select a URL for this publish.");
} else if (!this.claim()) {
return __("This URL is unused.");
- } else if (this.myClaimExists()) {
- return __(
- "You have already used this URL. Publishing to it again will update your previous publish."
+ } else if (this.myClaimExists() && !this.state.prefillDone) {
+ return (
+
+ {__("You already have a claim with this name.")}{" "}
+ this.handlePrefillClicked()}
+ />
+
);
- } else if (this.state.topClaimValue) {
- if (this.state.topClaimValue === 1) {
+ } else if (this.claim()) {
+ if (this.topClaimValue() === 1) {
return (
{__(
@@ -439,7 +428,7 @@ class PublishPage extends React.PureComponent {
{__(
'A deposit of at least "%s" credits is required to win "%s". However, you can still get a permanent URL for any amount.',
- this.state.topClaimValue,
+ this.topClaimValue(),
this.state.name
)}
@@ -709,77 +698,11 @@ class PublishPage extends React.PureComponent {
-
-
-
{__("Identity")}
-
- {__("Who created this content?")}
-
-
-
- {this.props.fetchingChannels
- ?
- : {
- this.handleChannelChange(event);
- }}
- value={this.state.channel}
- >
-
- {__("Anonymous")}
-
- {this.props.channels.map(({ name }) =>
- {name}
- )}
-
- {__("New identity...")}
-
- }
-
- {this.state.channel == "new"
- ?
-
{
- this.handleNewChannelNameChange(event);
- }}
- ref={newChannelName => {
- this.refs.newChannelName = newChannelName;
- }}
- value={this.state.newChannelName}
- />
- {
- this.handleNewChannelBidChange(event);
- }}
- value={this.state.newChannelBid}
- />
-
- {
- this.handleCreateChannelClick(event);
- }}
- disabled={this.state.creatingChannel}
- />
-
-
- : null}
-
+
@@ -795,7 +718,9 @@ class PublishPage extends React.PureComponent {
1 &&
+ !lbryuri.isValidName(newChannelName.substr(1), false)
+ ) {
+ this.refs.newChannelName.showError(
+ __("LBRY channel names must contain only letters, numbers and dashes.")
+ );
+ return;
+ } else {
+ this.refs.newChannelName.clearError();
+ }
+
+ this.setState({
+ newChannelName,
+ });
+ }
+
+ handleNewChannelBidChange(event) {
+ this.setState({
+ newChannelBid: event.target.value,
+ });
+ }
+
+ handleCreateChannelClick(event) {
+ if (this.state.newChannelName.length < 5) {
+ this.refs.newChannelName.showError(
+ __("LBRY channel names must be at least 4 characters in length.")
+ );
+ return;
+ }
+
+ this.setState({
+ creatingChannel: true,
+ });
+
+ const newChannelName = this.state.newChannelName;
+ const amount = parseFloat(this.state.newChannelBid);
+ this.setState({
+ creatingChannel: true,
+ });
+ const success = (() => {
+ this.setState({
+ creatingChannel: false,
+ addingChannel: false,
+ channel: newChannelName,
+ });
+ this.props.handleChannelChange(newChannelName);
+ }).bind(this);
+ const failure = (err => {
+ this.setState({
+ creatingChannel: false,
+ });
+ this.refs.newChannelName.showError(
+ __("Unable to create channel due to an internal error.")
+ );
+ }).bind(this);
+ this.props.createChannel(newChannelName, amount).then(success, failure);
+ }
+
+ render() {
+ const lbcInputHelp = __(
+ "This LBC remains yours and the deposit can be undone at any time."
+ );
+
+ const { fetchingChannels, channels } = this.props;
+
+ let channelContent = [];
+ if (channels.length > 0) {
+ channelContent.push(
+
+
+ {__("Anonymous")}
+
+ {this.props.channels.map(({ name }) =>
+ {name}
+ )}
+
+ {__("New identity...")}
+
+
+ );
+ if (fetchingChannels) {
+ channelContent.push(
+
+ );
+ }
+ } else if (fetchingChannels) {
+ channelContent.push(
+
+ );
+ }
+
+ return (
+
+
+
{__("Identity")}
+
+ {__("Who created this content?")}
+
+
+
+ {channelContent}
+
+ {this.state.addingChannel &&
+
+
{
+ this.handleNewChannelNameChange(event);
+ }}
+ value={this.state.newChannelName}
+ />
+
+
+
+
+ }
+
+ );
+ }
+}
+
export default PublishPage;
diff --git a/ui/js/reducers/claims.js b/ui/js/reducers/claims.js
index f71e2cadd..75bfc4a52 100644
--- a/ui/js/reducers/claims.js
+++ b/ui/js/reducers/claims.js
@@ -15,7 +15,13 @@ reducers[types.RESOLVE_URI_COMPLETED] = function(state, action) {
byUri[uri] = claim.claim_id;
} else if (claim === undefined && certificate !== undefined) {
byId[certificate.claim_id] = certificate;
- byUri[uri] = certificate.claim_id;
+ // Don't point URI at the channel certificate unless it actually is
+ // a channel URI. This is brittle.
+ if (!uri.split(certificate.name)[1].match(/\//)) {
+ byUri[uri] = certificate.claim_id;
+ } else {
+ byUri[uri] = null;
+ }
} else {
byUri[uri] = null;
}
@@ -108,6 +114,34 @@ reducers[types.ABANDON_CLAIM_COMPLETED] = function(state, action) {
});
};
+reducers[types.CREATE_CHANNEL_COMPLETED] = function(state, action) {
+ const { channelClaim } = action.data;
+ const byId = Object.assign({}, state.byId);
+ const myChannelClaims = new Set(state.myChannelClaims);
+
+ byId[channelClaim.claim_id] = channelClaim;
+ myChannelClaims.add(channelClaim.claim_id);
+
+ return Object.assign({}, state, {
+ byId,
+ myChannelClaims,
+ });
+};
+
+reducers[types.PUBLISH_COMPLETED] = function(state, action) {
+ const { claim } = action.data;
+ const byId = Object.assign({}, state.byId);
+ const myClaims = new Set(state.myClaims);
+
+ byId[claim.claim_id] = claim;
+ myClaims.add(claim.claim_id);
+
+ return Object.assign({}, state, {
+ byId,
+ myClaims,
+ });
+};
+
export default function reducer(state = defaultState, action) {
const handler = reducers[action.type];
if (handler) return handler(state, action);
diff --git a/ui/js/reducers/file_info.js b/ui/js/reducers/file_info.js
index 0f6b7a63d..fe6979045 100644
--- a/ui/js/reducers/file_info.js
+++ b/ui/js/reducers/file_info.js
@@ -12,8 +12,9 @@ reducers[types.FILE_LIST_STARTED] = function(state, action) {
reducers[types.FILE_LIST_COMPLETED] = function(state, action) {
const { fileInfos } = action.data;
-
const newByOutpoint = Object.assign({}, state.byOutpoint);
+ const pendingByOutpoint = Object.assign({}, state.pendingByOutpoint);
+
fileInfos.forEach(fileInfo => {
const { outpoint } = fileInfo;
@@ -23,6 +24,7 @@ reducers[types.FILE_LIST_COMPLETED] = function(state, action) {
return Object.assign({}, state, {
isFileListPending: false,
byOutpoint: newByOutpoint,
+ pendingByOutpoint,
});
};
@@ -136,6 +138,61 @@ reducers[types.LOADING_VIDEO_FAILED] = function(state, action) {
});
};
+reducers[types.PUBLISH_STARTED] = function(state, action) {
+ const { pendingPublish } = action.data;
+ const pendingByOutpoint = Object.assign({}, state.pendingByOutpoint);
+
+ pendingByOutpoint[pendingPublish.outpoint] = pendingPublish;
+
+ return Object.assign({}, state, {
+ pendingByOutpoint,
+ });
+};
+
+reducers[types.PUBLISH_COMPLETED] = function(state, action) {
+ const { pendingPublish } = action.data;
+ const pendingByOutpoint = Object.assign({}, state.pendingByOutpoint);
+
+ delete pendingByOutpoint[pendingPublish.outpoint];
+
+ return Object.assign({}, state, {
+ pendingByOutpoint,
+ });
+};
+
+reducers[types.PUBLISH_FAILED] = function(state, action) {
+ const { pendingPublish } = action.data;
+ const pendingByOutpoint = Object.assign({}, state.pendingByOutpoint);
+
+ delete pendingByOutpoint[pendingPublish.outpoint];
+
+ return Object.assign({}, state, {
+ pendingByOutpoint,
+ });
+};
+
+// reducers[types.PUBLISH_COMPLETED] = function(state, action) {
+// const { claim } = action.data;
+// const uri = lbryuri.build({
+// txid: claim.txId
+// })
+// const newPendingPublish = {
+// name,
+// channel_name,
+// claim_id: "pending_claim_" + uri,
+// txid: "pending_" + uri,
+// nout: 0,
+// outpoint: "pending_" + uri + ":0",
+// time: Date.now(),
+// };
+// const fileInfos = Object.assign({}, state.fileInfos)
+// fileInfos[newPendingPublish.outpoint] = newPendingPublish
+
+// return Object.assign({}, state, {
+// fileInfos,
+// })
+// }
+
export default function reducer(state = defaultState, action) {
const handler = reducers[action.type];
if (handler) return handler(state, action);
diff --git a/ui/js/selectors/file_info.js b/ui/js/selectors/file_info.js
index 552368f7e..5b0e4941b 100644
--- a/ui/js/selectors/file_info.js
+++ b/ui/js/selectors/file_info.js
@@ -69,6 +69,11 @@ export const makeSelectLoadingForUri = () => {
return createSelector(selectLoadingForUri, loading => !!loading);
};
+export const selectFileInfosPendingPublish = createSelector(
+ _selectState,
+ state => Object.values(state.pendingByOutpoint || {})
+);
+
export const selectFileInfosDownloaded = createSelector(
selectFileInfosByOutpoint,
selectMyClaimsOutpoints,
@@ -87,24 +92,17 @@ export const selectFileInfosDownloaded = createSelector(
}
);
-export const selectFileInfosPendingPublish = createSelector(
- _selectState,
- state => {
- return lbry.getPendingPublishes();
- }
-);
-
export const selectFileInfosPublished = createSelector(
selectFileInfosByOutpoint,
- selectFileInfosPendingPublish,
selectMyClaimsOutpoints,
- (byOutpoint, pendingFileInfos, outpoints) => {
+ selectFileInfosPendingPublish,
+ (byOutpoint, outpoints, pendingPublish) => {
const fileInfos = [];
outpoints.forEach(outpoint => {
const fileInfo = byOutpoint[outpoint];
if (fileInfo) fileInfos.push(fileInfo);
});
- return [...fileInfos, ...pendingFileInfos];
+ return fileInfos;
}
);
@@ -133,7 +131,6 @@ export const selectFileInfosByUri = createSelector(
if (fileInfo) fileInfos[uri] = fileInfo;
}
});
-
return fileInfos;
}
);
diff --git a/ui/js/store.js b/ui/js/store.js
index 1bb5ec058..8e6c11949 100644
--- a/ui/js/store.js
+++ b/ui/js/store.js
@@ -92,12 +92,16 @@ const saveClaimsFilter = createFilter("claims", [
"myClaims",
"myChannelClaims",
]);
+const saveFileInfosFilter = createFilter("fileInfo", [
+ "fileInfos",
+ "pendingByOutpoint",
+]);
const persistOptions = {
- whitelist: ["claims"],
+ whitelist: ["claims", "fileInfo"],
// Order is important. Needs to be compressed last or other transforms can't
// read the data
- transforms: [saveClaimsFilter, compressor],
+ transforms: [saveClaimsFilter, saveFileInfosFilter, compressor],
debounce: 1000,
storage: localForage,
};
diff --git a/ui/yarn.lock b/ui/yarn.lock
index 78dcb8080..cb2ca85fb 100644
--- a/ui/yarn.lock
+++ b/ui/yarn.lock
@@ -52,6 +52,15 @@ ajv@^4.7.0, ajv@^4.9.1:
co "^4.6.0"
json-stable-stringify "^1.0.1"
+ajv@^5.0.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.2.0.tgz#c1735024c5da2ef75cc190713073d44f098bf486"
+ dependencies:
+ co "^4.6.0"
+ fast-deep-equal "^0.1.0"
+ json-schema-traverse "^0.3.0"
+ json-stable-stringify "^1.0.1"
+
align-text@^0.1.1, align-text@^0.1.3:
version "0.1.4"
resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117"
@@ -60,6 +69,10 @@ align-text@^0.1.1, align-text@^0.1.3:
longest "^1.0.1"
repeat-string "^1.5.2"
+alphanum-sort@^1.0.1, alphanum-sort@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3"
+
amdefine@>=0.0.4:
version "1.0.1"
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
@@ -84,6 +97,10 @@ ansicolors@~0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.2.1.tgz#be089599097b74a5c9c4a84a0cdbcdb62bd87aef"
+any-promise@^1.0.0, any-promise@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
+
anymatch@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.0.tgz#a3e52fa39168c825ff57b0248126ce5a8ff95507"
@@ -193,6 +210,10 @@ ast-types@0.8.15:
version "0.8.15"
resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.8.15.tgz#8eef0827f04dff0ec8857ba925abe3fea6194e52"
+ast-types@0.9.6:
+ version "0.9.6"
+ resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.6.tgz#102c9e9e9005d3e7e3829bf0c4fa24ee862ee9b9"
+
async-each@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d"
@@ -223,6 +244,17 @@ asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+autoprefixer@^6.3.1:
+ version "6.7.7"
+ resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz#1dbd1c835658e35ce3f9984099db00585c782014"
+ dependencies:
+ browserslist "^1.7.6"
+ caniuse-db "^1.0.30000634"
+ normalize-range "^0.1.2"
+ num2fraction "^1.2.2"
+ postcss "^5.2.16"
+ postcss-value-parser "^3.2.3"
+
aws-sign2@~0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f"
@@ -252,7 +284,7 @@ babel-cli@^6.24.1:
optionalDependencies:
chokidar "^1.6.1"
-babel-code-frame@^6.16.0, babel-code-frame@^6.22.0:
+babel-code-frame@^6.11.0, babel-code-frame@^6.16.0, babel-code-frame@^6.22.0:
version "6.22.0"
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574e7fd0801d33118e4"
dependencies:
@@ -887,6 +919,10 @@ babylon@^6.11.5, babylon@^6.17.2:
version "6.17.4"
resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.17.4.tgz#3e8b7402b88d22c3423e137a1577883b15ff869a"
+balanced-match@^0.4.2:
+ version "0.4.2"
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838"
+
balanced-match@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
@@ -895,6 +931,10 @@ base62@0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/base62/-/base62-0.1.1.tgz#7b4174c2f94449753b11c2651c083da841a7b084"
+base62@^1.1.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/base62/-/base62-1.2.0.tgz#31e7e560dc846c9f44c1a531df6514da35474157"
+
base64-js@^1.0.2:
version "1.2.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.1.tgz#a91947da1f4a516ea38e5b4ec0ec3773675e0886"
@@ -1024,6 +1064,13 @@ browserify-zlib@^0.1.4:
dependencies:
pako "~0.2.0"
+browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6:
+ version "1.7.7"
+ resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz#0bd76704258be829b2398bb50e4b62d1a166b0b9"
+ dependencies:
+ caniuse-db "^1.0.30000639"
+ electron-to-chromium "^1.2.7"
+
buffer-indexof@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.0.tgz#f54f647c4f4e25228baa656a2e57e43d5f270982"
@@ -1081,6 +1128,19 @@ camelcase@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a"
+caniuse-api@^1.5.2:
+ version "1.6.1"
+ resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz#b534e7c734c4f81ec5fbe8aca2ad24354b962c6c"
+ dependencies:
+ browserslist "^1.3.6"
+ caniuse-db "^1.0.30000529"
+ lodash.memoize "^4.1.2"
+ lodash.uniq "^4.5.0"
+
+caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639:
+ version "1.0.30000694"
+ resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000694.tgz#02009f4f82d2f0126e4c691b7cd5adb351935c01"
+
cardinal@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/cardinal/-/cardinal-1.0.0.tgz#50e21c1b0aa37729f9377def196b5a9cec932ee9"
@@ -1138,16 +1198,32 @@ circular-json@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.1.tgz#be8b36aefccde8b3ca7aa2d6afc07a37242c0d2d"
+clap@^1.0.9:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/clap/-/clap-1.2.0.tgz#59c90fe3e137104746ff19469a27a634ff68c857"
+ dependencies:
+ chalk "^1.1.3"
+
cli-cursor@^1.0.1, cli-cursor@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987"
dependencies:
restore-cursor "^1.0.1"
+cli-cursor@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
+ dependencies:
+ restore-cursor "^2.0.0"
+
cli-spinners@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-0.1.2.tgz#bb764d88e185fb9e1e6a2a1f19772318f605e31c"
+cli-spinners@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-1.0.0.tgz#ef987ed3d48391ac3dab9180b406a742180d6e6a"
+
cli-table@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.1.tgz#f53b05266a8b1a0b934b3d0821e6e2dc5914ae23"
@@ -1196,21 +1272,73 @@ co@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
+coa@~1.0.1:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/coa/-/coa-1.0.3.tgz#1b54a5e1dcf77c990455d4deea98c564416dc893"
+ dependencies:
+ q "^1.1.2"
+
code-point-at@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
+codemirror-spell-checker@*:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/codemirror-spell-checker/-/codemirror-spell-checker-1.1.2.tgz#1c660f9089483ccb5113b9ba9ca19c3f4993371e"
+ dependencies:
+ typo-js "*"
+
+codemirror@*:
+ version "5.27.2"
+ resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.27.2.tgz#a292d42f079d5b98c68c3146fab99844f3d8776c"
+
+color-convert@^1.3.0:
+ version "1.9.0"
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.0.tgz#1accf97dd739b983bf994d56fec8f95853641b7a"
+ dependencies:
+ color-name "^1.1.1"
+
+color-name@^1.0.0, color-name@^1.1.1:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.2.tgz#5c8ab72b64bd2215d617ae9559ebb148475cf98d"
+
+color-string@^0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz#27d46fb67025c5c2fa25993bfbf579e47841b991"
+ dependencies:
+ color-name "^1.0.0"
+
+color@^0.11.0:
+ version "0.11.4"
+ resolved "https://registry.yarnpkg.com/color/-/color-0.11.4.tgz#6d7b5c74fb65e841cd48792ad1ed5e07b904d764"
+ dependencies:
+ clone "^1.0.2"
+ color-convert "^1.3.0"
+ color-string "^0.3.0"
+
+colormin@^1.0.5:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/colormin/-/colormin-1.1.2.tgz#ea2f7420a72b96881a38aae59ec124a6f7298133"
+ dependencies:
+ color "^0.11.0"
+ css-color-names "0.0.4"
+ has "^1.0.1"
+
colors@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b"
+colors@^1.1.2, colors@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
+
combined-stream@^1.0.5, combined-stream@~1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009"
dependencies:
delayed-stream "~1.0.0"
-commander@^2.8.1, commander@^2.9.0:
+commander@^2.5.0, commander@^2.8.1, commander@^2.9.0:
version "2.10.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.10.0.tgz#e1f5d3245de246d1a5ca04702fa1ad1bd7e405fe"
dependencies:
@@ -1220,6 +1348,38 @@ commondir@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
+commoner@^0.10.1:
+ version "0.10.8"
+ resolved "https://registry.yarnpkg.com/commoner/-/commoner-0.10.8.tgz#34fc3672cd24393e8bb47e70caa0293811f4f2c5"
+ dependencies:
+ commander "^2.5.0"
+ detective "^4.3.1"
+ glob "^5.0.15"
+ graceful-fs "^4.1.2"
+ iconv-lite "^0.4.5"
+ mkdirp "^0.5.0"
+ private "^0.1.6"
+ q "^1.1.2"
+ recast "^0.11.17"
+
+commonmark-react-renderer@^4.2.4:
+ version "4.3.3"
+ resolved "https://registry.yarnpkg.com/commonmark-react-renderer/-/commonmark-react-renderer-4.3.3.tgz#9c4bca138bc83287bae792ccf133738be9cbc6fa"
+ dependencies:
+ in-publish "^2.0.0"
+ lodash.assign "^4.2.0"
+ lodash.isplainobject "^4.0.6"
+ pascalcase "^0.1.1"
+ xss-filters "^1.2.6"
+
+commonmark@^0.24.0:
+ version "0.24.0"
+ resolved "https://registry.yarnpkg.com/commonmark/-/commonmark-0.24.0.tgz#b80de0182c546355643aa15db12bfb282368278f"
+ dependencies:
+ entities "~ 1.1.1"
+ mdurl "~ 1.0.1"
+ string.prototype.repeat "^0.2.0"
+
compressible@~2.0.8:
version "2.0.10"
resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.10.tgz#feda1c7f7617912732b29bf8cf26252a20b9eecd"
@@ -1396,6 +1556,85 @@ crypto-browserify@^3.11.0:
public-encrypt "^4.0.0"
randombytes "^2.0.0"
+css-color-names@0.0.4:
+ version "0.0.4"
+ resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
+
+css-loader@^0.28.4:
+ version "0.28.4"
+ resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.28.4.tgz#6cf3579192ce355e8b38d5f42dd7a1f2ec898d0f"
+ dependencies:
+ babel-code-frame "^6.11.0"
+ css-selector-tokenizer "^0.7.0"
+ cssnano ">=2.6.1 <4"
+ icss-utils "^2.1.0"
+ loader-utils "^1.0.2"
+ lodash.camelcase "^4.3.0"
+ object-assign "^4.0.1"
+ postcss "^5.0.6"
+ postcss-modules-extract-imports "^1.0.0"
+ postcss-modules-local-by-default "^1.0.1"
+ postcss-modules-scope "^1.0.0"
+ postcss-modules-values "^1.1.0"
+ postcss-value-parser "^3.3.0"
+ source-list-map "^0.1.7"
+
+css-selector-tokenizer@^0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz#e6988474ae8c953477bf5e7efecfceccd9cf4c86"
+ dependencies:
+ cssesc "^0.1.0"
+ fastparse "^1.1.1"
+ regexpu-core "^1.0.0"
+
+cssesc@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4"
+
+"cssnano@>=2.6.1 <4":
+ version "3.10.0"
+ resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-3.10.0.tgz#4f38f6cea2b9b17fa01490f23f1dc68ea65c1c38"
+ dependencies:
+ autoprefixer "^6.3.1"
+ decamelize "^1.1.2"
+ defined "^1.0.0"
+ has "^1.0.1"
+ object-assign "^4.0.1"
+ postcss "^5.0.14"
+ postcss-calc "^5.2.0"
+ postcss-colormin "^2.1.8"
+ postcss-convert-values "^2.3.4"
+ postcss-discard-comments "^2.0.4"
+ postcss-discard-duplicates "^2.0.1"
+ postcss-discard-empty "^2.0.1"
+ postcss-discard-overridden "^0.1.1"
+ postcss-discard-unused "^2.2.1"
+ postcss-filter-plugins "^2.0.0"
+ postcss-merge-idents "^2.1.5"
+ postcss-merge-longhand "^2.0.1"
+ postcss-merge-rules "^2.0.3"
+ postcss-minify-font-values "^1.0.2"
+ postcss-minify-gradients "^1.0.1"
+ postcss-minify-params "^1.0.4"
+ postcss-minify-selectors "^2.0.4"
+ postcss-normalize-charset "^1.1.0"
+ postcss-normalize-url "^3.0.7"
+ postcss-ordered-values "^2.1.0"
+ postcss-reduce-idents "^2.2.2"
+ postcss-reduce-initial "^1.0.0"
+ postcss-reduce-transforms "^1.0.3"
+ postcss-svgo "^2.1.1"
+ postcss-unique-selectors "^2.0.2"
+ postcss-value-parser "^3.2.3"
+ postcss-zindex "^2.0.1"
+
+csso@~2.3.1:
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/csso/-/csso-2.3.2.tgz#ddd52c587033f49e94b71fc55569f252e8ff5f85"
+ dependencies:
+ clap "^1.0.9"
+ source-map "^0.5.3"
+
currently-unhandled@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea"
@@ -1432,7 +1671,7 @@ debug@2.6.7:
dependencies:
ms "2.0.0"
-debug@2.6.8, debug@^2.1.1, debug@^2.2.0, debug@^2.6.8:
+debug@2.6.8, debug@^2.1.1, debug@^2.2.0, debug@^2.5.1, debug@^2.6.3, debug@^2.6.8:
version "2.6.8"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc"
dependencies:
@@ -1471,6 +1710,10 @@ define-properties@^1.1.2:
foreach "^2.0.5"
object-keys "^1.0.8"
+defined@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
+
del@^2.0.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8"
@@ -1527,6 +1770,13 @@ detect-node@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.3.tgz#a2033c09cc8e158d37748fbde7507832bd6ce127"
+detective@^4.3.1:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/detective/-/detective-4.5.0.tgz#6e5a8c6b26e6c7a254b1c6b6d7490d98ec91edd1"
+ dependencies:
+ acorn "^4.0.3"
+ defined "^1.0.0"
+
diffie-hellman@^5.0.0:
version "5.0.2"
resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.2.tgz#b5835739270cfe26acf632099fded2a07f209e5e"
@@ -1580,6 +1830,24 @@ ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
+electron-rebuild@^1.5.11:
+ version "1.5.11"
+ resolved "https://registry.yarnpkg.com/electron-rebuild/-/electron-rebuild-1.5.11.tgz#6ea660deb546a516e7efaa81cd5985d5664f245c"
+ dependencies:
+ colors "^1.1.2"
+ debug "^2.6.3"
+ fs-promise "^2.0.2"
+ node-abi "^2.0.0"
+ node-gyp "^3.6.0"
+ ora "^1.2.0"
+ rimraf "^2.6.1"
+ spawn-rx "^2.0.10"
+ yargs "^7.0.2"
+
+electron-to-chromium@^1.2.7:
+ version "1.3.14"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.14.tgz#64af0f9efd3c3c6acd57d71f83b49ca7ee9c4b43"
+
elegant-spinner@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e"
@@ -1637,6 +1905,17 @@ enhanced-resolve@~0.9.0:
memory-fs "^0.2.0"
tapable "^0.1.8"
+"entities@~ 1.1.1":
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0"
+
+envify@^3.0.0:
+ version "3.4.1"
+ resolved "https://registry.yarnpkg.com/envify/-/envify-3.4.1.tgz#d7122329e8df1688ba771b12501917c9ce5cbce8"
+ dependencies:
+ jstransform "^11.0.3"
+ through "~2.3.4"
+
errno@^0.1.3:
version "0.1.4"
resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.4.tgz#b896e23a9e5e8ba33871fc996abd3635fc9a1c7d"
@@ -1862,6 +2141,10 @@ espree@^3.4.0:
acorn "^5.0.1"
acorn-jsx "^3.0.0"
+esprima-fb@^15001.1.0-dev-harmony-fb:
+ version "15001.1.0-dev-harmony-fb"
+ resolved "https://registry.yarnpkg.com/esprima-fb/-/esprima-fb-15001.1.0-dev-harmony-fb.tgz#30a947303c6b8d5e955bee2b99b1d233206a6901"
+
esprima-fb@~15001.1001.0-dev-harmony-fb:
version "15001.1001.0-dev-harmony-fb"
resolved "https://registry.yarnpkg.com/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz#43beb57ec26e8cf237d3dd8b33e42533577f2659"
@@ -1870,7 +2153,11 @@ esprima-fb@~3001.0001.0000-dev-harmony-fb, esprima-fb@~3001.1.0-dev-harmony-fb:
version "3001.1.0-dev-harmony-fb"
resolved "https://registry.yarnpkg.com/esprima-fb/-/esprima-fb-3001.0001.0000-dev-harmony-fb.tgz#b77d37abcd38ea0b77426bb8bc2922ce6b426411"
-esprima@^3.1.1:
+esprima@^2.6.0:
+ version "2.7.3"
+ resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581"
+
+esprima@^3.1.1, esprima@~3.1.0:
version "3.1.3"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
@@ -2018,10 +2305,18 @@ falafel@^1.0.1:
isarray "0.0.1"
object-keys "^1.0.6"
+fast-deep-equal@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-0.1.0.tgz#5c6f4599aba6b333ee3342e2ed978672f1001f8d"
+
fast-levenshtein@~2.0.4:
version "2.0.6"
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+fastparse@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8"
+
faye-websocket@^0.10.0:
version "0.10.0"
resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4"
@@ -2034,6 +2329,16 @@ faye-websocket@~0.11.0:
dependencies:
websocket-driver ">=0.5.1"
+fbjs@^0.6.1:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.6.1.tgz#9636b7705f5ba9684d44b72f78321254afc860f7"
+ dependencies:
+ core-js "^1.0.0"
+ loose-envify "^1.0.0"
+ promise "^7.0.3"
+ ua-parser-js "^0.7.9"
+ whatwg-fetch "^0.9.0"
+
fbjs@^0.8.9:
version "0.8.12"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.12.tgz#10b5d92f76d45575fd63a217d4ea02bea2f8ed04"
@@ -2120,6 +2425,10 @@ flat-cache@^1.2.1:
graceful-fs "^4.1.2"
write "^0.2.1"
+flatten@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782"
+
for-in@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
@@ -2161,6 +2470,22 @@ from2@^2.3.0:
inherits "^2.0.1"
readable-stream "^2.0.0"
+fs-extra@^2.0.0:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-2.1.2.tgz#046c70163cef9aad46b0e4a7fa467fb22d71de35"
+ dependencies:
+ graceful-fs "^4.1.2"
+ jsonfile "^2.1.0"
+
+fs-promise@^2.0.2:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/fs-promise/-/fs-promise-2.0.3.tgz#f64e4f854bcf689aa8bddcba268916db3db46854"
+ dependencies:
+ any-promise "^1.3.0"
+ fs-extra "^2.0.0"
+ mz "^2.6.0"
+ thenify-all "^1.6.0"
+
fs-readdir-recursive@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.0.0.tgz#8cd1745c8b4f8a29c8caec392476921ba195f560"
@@ -2263,6 +2588,16 @@ glob-parent@^2.0.0:
dependencies:
is-glob "^2.0.0"
+glob@^5.0.15:
+ version "5.0.15"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1"
+ dependencies:
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "2 || 3"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@~7.1.1:
version "7.1.2"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
@@ -2307,7 +2642,7 @@ globule@^1.0.0:
lodash "~4.17.4"
minimatch "~3.0.2"
-graceful-fs@^4.1.2, graceful-fs@^4.1.4:
+graceful-fs@^4.1.2, graceful-fs@^4.1.4, graceful-fs@^4.1.6:
version "4.1.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
@@ -2344,6 +2679,10 @@ has-flag@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
+has-flag@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51"
+
has-unicode@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
@@ -2412,6 +2751,10 @@ hpack.js@^2.1.6:
readable-stream "^2.0.1"
wbuf "^1.1.0"
+html-comment-regex@^1.1.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.1.tgz#668b93776eaae55ebde8f3ad464b307a4963625e"
+
html-entities@^1.2.0:
version "1.2.1"
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f"
@@ -2475,10 +2818,20 @@ i18n-extract@^0.4.4:
gettext-parser "^1.2.0"
glob "^7.1.1"
-iconv-lite@~0.4.13:
+iconv-lite@^0.4.5, iconv-lite@~0.4.13:
version "0.4.18"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.18.tgz#23d8656b16aae6742ac29732ea8f0336a4789cf2"
+icss-replace-symbols@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded"
+
+icss-utils@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-2.1.0.tgz#83f0a0ec378bf3246178b6c2ad9136f135b1c962"
+ dependencies:
+ postcss "^6.0.1"
+
ieee754@^1.1.4:
version "1.1.8"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4"
@@ -2509,6 +2862,10 @@ indent-string@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.1.0.tgz#08ff4334603388399b329e6b9538dc7a3cf5de7d"
+indexes-of@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607"
+
indexof@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d"
@@ -2589,6 +2946,10 @@ ipaddr.js@1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.3.0.tgz#1e03a52fdad83a8bbb2b25cbf4998b4cffcd3dec"
+is-absolute-url@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6"
+
is-arrayish@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
@@ -2714,6 +3075,10 @@ is-path-inside@^1.0.0:
dependencies:
path-is-inside "^1.0.1"
+is-plain-obj@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
+
is-posix-bracket@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4"
@@ -2746,6 +3111,12 @@ is-stream@^1.0.1, is-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
+is-svg@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-2.1.0.tgz#cf61090da0d9efbcab8722deba6f032208dbb0e9"
+ dependencies:
+ html-comment-regex "^1.1.0"
+
is-symbol@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572"
@@ -2787,7 +3158,7 @@ isstream@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
-js-base64@^2.1.8:
+js-base64@^2.1.8, js-base64@^2.1.9:
version "2.1.9"
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.1.9.tgz#f0e80ae039a4bd654b5f281fc93f04a914a7fcce"
@@ -2802,6 +3173,13 @@ js-yaml@^3.4.3, js-yaml@^3.5.1:
argparse "^1.0.7"
esprima "^3.1.1"
+js-yaml@~3.7.0:
+ version "3.7.0"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80"
+ dependencies:
+ argparse "^1.0.7"
+ esprima "^2.6.0"
+
jsbn@~0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
@@ -2822,6 +3200,10 @@ json-loader@^0.5.4:
version "0.5.4"
resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.4.tgz#8baa1365a632f58a3c46d20175fc6002c96e37de"
+json-schema-traverse@^0.3.0:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340"
+
json-schema@0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
@@ -2844,6 +3226,12 @@ json5@^0.5.0, json5@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
+jsonfile@^2.1.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8"
+ optionalDependencies:
+ graceful-fs "^4.1.6"
+
jsonify@~0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
@@ -2861,6 +3249,16 @@ jsprim@^1.2.2:
json-schema "0.2.3"
verror "1.3.6"
+jstransform@^11.0.3:
+ version "11.0.3"
+ resolved "https://registry.yarnpkg.com/jstransform/-/jstransform-11.0.3.tgz#09a78993e0ae4d4ef4487f6155a91f6190cb4223"
+ dependencies:
+ base62 "^1.1.0"
+ commoner "^0.10.1"
+ esprima-fb "^15001.1.0-dev-harmony-fb"
+ object-assign "^2.0.0"
+ source-map "^0.4.2"
+
jstransform@~3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/jstransform/-/jstransform-3.0.0.tgz#a2591ab6cee8d97bf3be830dbfa2313b87cd640b"
@@ -3082,6 +3480,10 @@ lodash.assign@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7"
+lodash.camelcase@^4.3.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
+
lodash.chunk@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/lodash.chunk/-/lodash.chunk-4.2.0.tgz#66e5ce1f76ed27b4303d8c6512e8d1216e8106bc"
@@ -3121,6 +3523,10 @@ lodash.isempty@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e"
+lodash.isplainobject@^4.0.6:
+ version "4.0.6"
+ resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
+
lodash.keys@^3.0.0:
version "3.1.2"
resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a"
@@ -3129,6 +3535,10 @@ lodash.keys@^3.0.0:
lodash.isarguments "^3.0.0"
lodash.isarray "^3.0.0"
+lodash.memoize@^4.1.2:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
+
lodash.mergewith@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz#150cf0a16791f5903b8891eab154609274bdea55"
@@ -3141,6 +3551,10 @@ lodash.set@^4.3.2:
version "4.3.2"
resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23"
+lodash.uniq@^4.5.0:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
+
lodash.unset@^4.5.2:
version "4.5.2"
resolved "https://registry.yarnpkg.com/lodash.unset/-/lodash.unset-4.5.2.tgz#370d1d3e85b72a7e1b0cdf2d272121306f23e4ed"
@@ -3194,6 +3608,10 @@ lz-string@^1.4.4:
version "1.4.4"
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26"
+macaddress@^0.2.8:
+ version "0.2.8"
+ resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12"
+
map-obj@^1.0.0, map-obj@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d"
@@ -3208,10 +3626,18 @@ marked-terminal@^1.6.2:
lodash.assign "^4.2.0"
node-emoji "^1.4.1"
-marked@^0.3.6:
+marked@*, marked@^0.3.6:
version "0.3.6"
resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.6.tgz#b2c6c618fccece4ef86c4fc6cb8a7cbf5aeda8d7"
+math-expression-evaluator@^1.2.14:
+ version "1.2.17"
+ resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz#de819fdbcd84dccd8fae59c6aeb79615b9d266ac"
+
+"mdurl@~ 1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
+
media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
@@ -3308,6 +3734,10 @@ mime@^1.3.4:
version "1.3.6"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.6.tgz#591d84d3653a6b0b4a3b9df8de5aa8108e72e5e0"
+mimic-fn@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18"
+
minimalistic-assert@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3"
@@ -3316,7 +3746,7 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
-minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.2:
+"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.2:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
dependencies:
@@ -3330,7 +3760,7 @@ minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
-mkdirp@0.5.1, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0:
+mkdirp@0.5.1, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
dependencies:
@@ -3381,6 +3811,14 @@ mute-stream@0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0"
+mz@^2.6.0:
+ version "2.6.0"
+ resolved "https://registry.yarnpkg.com/mz/-/mz-2.6.0.tgz#c8b8521d958df0a4f2768025db69c719ee4ef1ce"
+ dependencies:
+ any-promise "^1.0.0"
+ object-assign "^4.0.1"
+ thenify-all "^1.0.0"
+
nan@^2.3.0, nan@^2.3.2:
version "2.5.1"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.5.1.tgz#d5b01691253326a97a2bbee9e61c55d8d60351e2"
@@ -3397,6 +3835,10 @@ next-event@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/next-event/-/next-event-1.0.0.tgz#e7778acde2e55802e0ad1879c39cf6f75eda61d8"
+node-abi@^2.0.0:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.0.3.tgz#0ca67e5e667b8e1343549ca17153a815d0bbfdaa"
+
node-emoji@^1.4.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.5.1.tgz#fd918e412769bf8c448051238233840b2aff16a1"
@@ -3414,7 +3856,7 @@ node-forge@0.6.33:
version "0.6.33"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.6.33.tgz#463811879f573d45155ad6a9f43dc296e8e85ebc"
-node-gyp@^3.3.1:
+node-gyp@^3.3.1, node-gyp@^3.6.0:
version "3.6.2"
resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.6.2.tgz#9bfbe54562286284838e750eac05295853fa1c60"
dependencies:
@@ -3573,6 +4015,19 @@ normalize-path@^2.0.1:
dependencies:
remove-trailing-separator "^1.0.1"
+normalize-range@^0.1.2:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942"
+
+normalize-url@^1.4.0:
+ version "1.9.1"
+ resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c"
+ dependencies:
+ object-assign "^4.0.1"
+ prepend-http "^1.0.0"
+ query-string "^4.1.0"
+ sort-keys "^1.0.0"
+
npm-path@^2.0.2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/npm-path/-/npm-path-2.0.3.tgz#15cff4e1c89a38da77f56f6055b24f975dfb2bbe"
@@ -3602,6 +4057,10 @@ npm-which@^3.0.1:
gauge "~2.7.3"
set-blocking "~2.0.0"
+num2fraction@^1.2.2:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede"
+
number-is-nan@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
@@ -3610,6 +4069,10 @@ oauth-sign@~0.8.1:
version "0.8.2"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
+object-assign@^2.0.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-2.1.1.tgz#43c36e5d569ff8e4816c4efa8be02d26967c18aa"
+
object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
@@ -3661,6 +4124,12 @@ onetime@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789"
+onetime@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4"
+ dependencies:
+ mimic-fn "^1.0.0"
+
opn@4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/opn/-/opn-4.0.2.tgz#7abc22e644dff63b0a96d5ab7f2790c0f01abc95"
@@ -3695,6 +4164,15 @@ ora@^0.2.3:
cli-spinners "^0.1.2"
object-assign "^4.0.1"
+ora@^1.2.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/ora/-/ora-1.3.0.tgz#80078dd2b92a934af66a3ad72a5b910694ede51a"
+ dependencies:
+ chalk "^1.1.1"
+ cli-cursor "^2.1.0"
+ cli-spinners "^1.0.0"
+ log-symbols "^1.0.2"
+
original@>=0.0.5:
version "1.0.0"
resolved "https://registry.yarnpkg.com/original/-/original-1.0.0.tgz#9147f93fa1696d04be61e01bd50baeaca656bd3b"
@@ -3785,6 +4263,10 @@ parseurl@~1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.1.tgz#c8ab8c9223ba34888aa64a297b28853bec18da56"
+pascalcase@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"
+
path-browserify@0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a"
@@ -3887,10 +4369,260 @@ portfinder@^1.0.9:
debug "^2.2.0"
mkdirp "0.5.x"
+postcss-calc@^5.2.0:
+ version "5.3.1"
+ resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz#77bae7ca928ad85716e2fda42f261bf7c1d65b5e"
+ dependencies:
+ postcss "^5.0.2"
+ postcss-message-helpers "^2.0.0"
+ reduce-css-calc "^1.2.6"
+
+postcss-colormin@^2.1.8:
+ version "2.2.2"
+ resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-2.2.2.tgz#6631417d5f0e909a3d7ec26b24c8a8d1e4f96e4b"
+ dependencies:
+ colormin "^1.0.5"
+ postcss "^5.0.13"
+ postcss-value-parser "^3.2.3"
+
+postcss-convert-values@^2.3.4:
+ version "2.6.1"
+ resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz#bbd8593c5c1fd2e3d1c322bb925dcae8dae4d62d"
+ dependencies:
+ postcss "^5.0.11"
+ postcss-value-parser "^3.1.2"
+
+postcss-discard-comments@^2.0.4:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz#befe89fafd5b3dace5ccce51b76b81514be00e3d"
+ dependencies:
+ postcss "^5.0.14"
+
+postcss-discard-duplicates@^2.0.1:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz#b9abf27b88ac188158a5eb12abcae20263b91932"
+ dependencies:
+ postcss "^5.0.4"
+
+postcss-discard-empty@^2.0.1:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz#d2b4bd9d5ced5ebd8dcade7640c7d7cd7f4f92b5"
+ dependencies:
+ postcss "^5.0.14"
+
+postcss-discard-overridden@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz#8b1eaf554f686fb288cd874c55667b0aa3668d58"
+ dependencies:
+ postcss "^5.0.16"
+
+postcss-discard-unused@^2.2.1:
+ version "2.2.3"
+ resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz#bce30b2cc591ffc634322b5fb3464b6d934f4433"
+ dependencies:
+ postcss "^5.0.14"
+ uniqs "^2.0.0"
+
+postcss-filter-plugins@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz#6d85862534d735ac420e4a85806e1f5d4286d84c"
+ dependencies:
+ postcss "^5.0.4"
+ uniqid "^4.0.0"
+
+postcss-merge-idents@^2.1.5:
+ version "2.1.7"
+ resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz#4c5530313c08e1d5b3bbf3d2bbc747e278eea270"
+ dependencies:
+ has "^1.0.1"
+ postcss "^5.0.10"
+ postcss-value-parser "^3.1.1"
+
+postcss-merge-longhand@^2.0.1:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz#23d90cd127b0a77994915332739034a1a4f3d658"
+ dependencies:
+ postcss "^5.0.4"
+
+postcss-merge-rules@^2.0.3:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz#d1df5dfaa7b1acc3be553f0e9e10e87c61b5f721"
+ dependencies:
+ browserslist "^1.5.2"
+ caniuse-api "^1.5.2"
+ postcss "^5.0.4"
+ postcss-selector-parser "^2.2.2"
+ vendors "^1.0.0"
+
+postcss-message-helpers@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz#a4f2f4fab6e4fe002f0aed000478cdf52f9ba60e"
+
+postcss-minify-font-values@^1.0.2:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz#4b58edb56641eba7c8474ab3526cafd7bbdecb69"
+ dependencies:
+ object-assign "^4.0.1"
+ postcss "^5.0.4"
+ postcss-value-parser "^3.0.2"
+
+postcss-minify-gradients@^1.0.1:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz#5dbda11373703f83cfb4a3ea3881d8d75ff5e6e1"
+ dependencies:
+ postcss "^5.0.12"
+ postcss-value-parser "^3.3.0"
+
+postcss-minify-params@^1.0.4:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz#ad2ce071373b943b3d930a3fa59a358c28d6f1f3"
+ dependencies:
+ alphanum-sort "^1.0.1"
+ postcss "^5.0.2"
+ postcss-value-parser "^3.0.2"
+ uniqs "^2.0.0"
+
+postcss-minify-selectors@^2.0.4:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz#b2c6a98c0072cf91b932d1a496508114311735bf"
+ dependencies:
+ alphanum-sort "^1.0.2"
+ has "^1.0.1"
+ postcss "^5.0.14"
+ postcss-selector-parser "^2.0.0"
+
+postcss-modules-extract-imports@^1.0.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.0.tgz#66140ecece38ef06bf0d3e355d69bf59d141ea85"
+ dependencies:
+ postcss "^6.0.1"
+
+postcss-modules-local-by-default@^1.0.1:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz#f7d80c398c5a393fa7964466bd19500a7d61c069"
+ dependencies:
+ css-selector-tokenizer "^0.7.0"
+ postcss "^6.0.1"
+
+postcss-modules-scope@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz#d6ea64994c79f97b62a72b426fbe6056a194bb90"
+ dependencies:
+ css-selector-tokenizer "^0.7.0"
+ postcss "^6.0.1"
+
+postcss-modules-values@^1.1.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz#ecffa9d7e192518389f42ad0e83f72aec456ea20"
+ dependencies:
+ icss-replace-symbols "^1.1.0"
+ postcss "^6.0.1"
+
+postcss-normalize-charset@^1.1.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz#ef9ee71212d7fe759c78ed162f61ed62b5cb93f1"
+ dependencies:
+ postcss "^5.0.5"
+
+postcss-normalize-url@^3.0.7:
+ version "3.0.8"
+ resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz#108f74b3f2fcdaf891a2ffa3ea4592279fc78222"
+ dependencies:
+ is-absolute-url "^2.0.0"
+ normalize-url "^1.4.0"
+ postcss "^5.0.14"
+ postcss-value-parser "^3.2.3"
+
+postcss-ordered-values@^2.1.0:
+ version "2.2.3"
+ resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz#eec6c2a67b6c412a8db2042e77fe8da43f95c11d"
+ dependencies:
+ postcss "^5.0.4"
+ postcss-value-parser "^3.0.1"
+
+postcss-reduce-idents@^2.2.2:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz#c2c6d20cc958284f6abfbe63f7609bf409059ad3"
+ dependencies:
+ postcss "^5.0.4"
+ postcss-value-parser "^3.0.2"
+
+postcss-reduce-initial@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz#68f80695f045d08263a879ad240df8dd64f644ea"
+ dependencies:
+ postcss "^5.0.4"
+
+postcss-reduce-transforms@^1.0.3:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz#ff76f4d8212437b31c298a42d2e1444025771ae1"
+ dependencies:
+ has "^1.0.1"
+ postcss "^5.0.8"
+ postcss-value-parser "^3.0.1"
+
+postcss-selector-parser@^2.0.0, postcss-selector-parser@^2.2.2:
+ version "2.2.3"
+ resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz#f9437788606c3c9acee16ffe8d8b16297f27bb90"
+ dependencies:
+ flatten "^1.0.2"
+ indexes-of "^1.0.1"
+ uniq "^1.0.1"
+
+postcss-svgo@^2.1.1:
+ version "2.1.6"
+ resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-2.1.6.tgz#b6df18aa613b666e133f08adb5219c2684ac108d"
+ dependencies:
+ is-svg "^2.0.0"
+ postcss "^5.0.14"
+ postcss-value-parser "^3.2.3"
+ svgo "^0.7.0"
+
+postcss-unique-selectors@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz#981d57d29ddcb33e7b1dfe1fd43b8649f933ca1d"
+ dependencies:
+ alphanum-sort "^1.0.1"
+ postcss "^5.0.4"
+ uniqs "^2.0.0"
+
+postcss-value-parser@^3.0.1, postcss-value-parser@^3.0.2, postcss-value-parser@^3.1.1, postcss-value-parser@^3.1.2, postcss-value-parser@^3.2.3, postcss-value-parser@^3.3.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz#87f38f9f18f774a4ab4c8a232f5c5ce8872a9d15"
+
+postcss-zindex@^2.0.1:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-2.2.0.tgz#d2109ddc055b91af67fc4cb3b025946639d2af22"
+ dependencies:
+ has "^1.0.1"
+ postcss "^5.0.4"
+ uniqs "^2.0.0"
+
+postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0.14, postcss@^5.0.16, postcss@^5.0.2, postcss@^5.0.4, postcss@^5.0.5, postcss@^5.0.6, postcss@^5.0.8, postcss@^5.2.16:
+ version "5.2.17"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.17.tgz#cf4f597b864d65c8a492b2eabe9d706c879c388b"
+ dependencies:
+ chalk "^1.1.3"
+ js-base64 "^2.1.9"
+ source-map "^0.5.6"
+ supports-color "^3.2.3"
+
+postcss@^6.0.1:
+ version "6.0.3"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.3.tgz#b7f565b3d956fbb8565ca7c1e239d0506e427d8b"
+ dependencies:
+ chalk "^1.1.3"
+ source-map "^0.5.6"
+ supports-color "^4.0.0"
+
prelude-ls@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
+prepend-http@^1.0.0:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
+
preserve@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
@@ -3915,13 +4647,13 @@ progress@^1.1.8:
version "1.1.8"
resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be"
-promise@^7.1.1:
+promise@^7.0.3, promise@^7.1.1:
version "7.3.1"
resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
dependencies:
asap "~2.0.3"
-prop-types@^15.5.10, prop-types@^15.5.7, prop-types@^15.5.8:
+prop-types@^15.5.1, prop-types@^15.5.10, prop-types@^15.5.7, prop-types@^15.5.8:
version "15.5.10"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154"
dependencies:
@@ -3968,10 +4700,21 @@ punycode@^1.2.4, punycode@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
+q@^1.1.2:
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/q/-/q-1.5.0.tgz#dd01bac9d06d30e6f219aecb8253ee9ebdc308f1"
+
qs@6.4.0, qs@~6.4.0:
version "6.4.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233"
+query-string@^4.1.0:
+ version "4.3.4"
+ resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb"
+ dependencies:
+ object-assign "^4.1.0"
+ strict-uri-encode "^1.0.0"
+
querystring-es3@^0.2.0:
version "0.2.1"
resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
@@ -4041,6 +4784,15 @@ react-dom@^15.4.0:
object-assign "^4.1.0"
prop-types "^15.5.10"
+react-markdown@^2.5.0:
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-2.5.0.tgz#b1c61904fee5895886803bd9df7db23c3dc3a89e"
+ dependencies:
+ commonmark "^0.24.0"
+ commonmark-react-renderer "^4.2.4"
+ in-publish "^2.0.0"
+ prop-types "^15.5.1"
+
react-modal@^1.5.2:
version "1.9.7"
resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-1.9.7.tgz#07ef56790b953e3b98ef1e2989e347983c72871d"
@@ -4064,6 +4816,20 @@ react-redux@^5.0.3:
loose-envify "^1.1.0"
prop-types "^15.5.10"
+react-simplemde-editor@^3.6.11:
+ version "3.6.11"
+ resolved "https://registry.yarnpkg.com/react-simplemde-editor/-/react-simplemde-editor-3.6.11.tgz#4b9e136f6d4d00218e8ece3d87949e23b14e21dc"
+ dependencies:
+ react "^0.14.2"
+ simplemde "^1.11.2"
+
+react@^0.14.2:
+ version "0.14.9"
+ resolved "https://registry.yarnpkg.com/react/-/react-0.14.9.tgz#9110a6497c49d44ba1c0edd317aec29c2e0d91d1"
+ dependencies:
+ envify "^3.0.0"
+ fbjs "^0.6.1"
+
react@^15.4.0:
version "15.6.1"
resolved "https://registry.yarnpkg.com/react/-/react-15.6.1.tgz#baa8434ec6780bde997cdc380b79cd33b96393df"
@@ -4151,6 +4917,15 @@ recast@^0.10.1:
private "~0.1.5"
source-map "~0.5.0"
+recast@^0.11.17:
+ version "0.11.23"
+ resolved "https://registry.yarnpkg.com/recast/-/recast-0.11.23.tgz#451fd3004ab1e4df9b4e4b66376b2a21912462d3"
+ dependencies:
+ ast-types "0.9.6"
+ esprima "~3.1.0"
+ private "~0.1.5"
+ source-map "~0.5.0"
+
rechoir@^0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384"
@@ -4170,6 +4945,20 @@ redeyed@~1.0.0:
dependencies:
esprima "~3.0.0"
+reduce-css-calc@^1.2.6:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716"
+ dependencies:
+ balanced-match "^0.4.2"
+ math-expression-evaluator "^1.2.14"
+ reduce-function-call "^1.0.1"
+
+reduce-function-call@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/reduce-function-call/-/reduce-function-call-1.0.2.tgz#5a200bf92e0e37751752fe45b0ab330fd4b6be99"
+ dependencies:
+ balanced-match "^0.4.2"
+
redux-action-buffer@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/redux-action-buffer/-/redux-action-buffer-1.1.0.tgz#9c692ab6532b042d0d43a9f01a48ada120fc941a"
@@ -4242,6 +5031,14 @@ regex-cache@^0.4.2:
is-equal-shallow "^0.1.3"
is-primitive "^2.0.0"
+regexpu-core@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b"
+ dependencies:
+ regenerate "^1.2.1"
+ regjsgen "^0.2.0"
+ regjsparser "^0.1.4"
+
regexpu-core@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240"
@@ -4359,6 +5156,13 @@ restore-cursor@^1.0.1:
exit-hook "^1.0.0"
onetime "^1.0.0"
+restore-cursor@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
+ dependencies:
+ onetime "^2.0.0"
+ signal-exit "^3.0.2"
+
right-align@^0.1.1:
version "0.1.3"
resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef"
@@ -4392,7 +5196,7 @@ rx-lite@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102"
-rxjs@^5.0.0-beta.11:
+rxjs@^5.0.0-beta.11, rxjs@^5.1.1:
version "5.4.1"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.4.1.tgz#b62f757f279445d265a18a58fb0a70dc90e91626"
dependencies:
@@ -4411,6 +5215,16 @@ sass-graph@^2.1.1:
scss-tokenizer "^0.2.3"
yargs "^7.0.0"
+sax@~1.2.1:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
+
+schema-utils@^0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.3.0.tgz#f5877222ce3e931edae039f17eb3716e7137f8cf"
+ dependencies:
+ ajv "^5.0.0"
+
scss-tokenizer@^0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1"
@@ -4519,10 +5333,18 @@ shellwords@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.0.tgz#66afd47b6a12932d9071cbfd98a52e785cd0ba14"
-signal-exit@^3.0.0:
+signal-exit@^3.0.0, signal-exit@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
+simplemde@^1.11.2:
+ version "1.11.2"
+ resolved "https://registry.yarnpkg.com/simplemde/-/simplemde-1.11.2.tgz#a23a35d978d2c40ef07dec008c92f070d8e080e3"
+ dependencies:
+ codemirror "*"
+ codemirror-spell-checker "*"
+ marked "*"
+
slash@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
@@ -4555,14 +5377,20 @@ sockjs@0.3.18:
faye-websocket "^0.10.0"
uuid "^2.0.2"
+sort-keys@^1.0.0:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad"
+ dependencies:
+ is-plain-obj "^1.0.0"
+
+source-list-map@^0.1.7, source-list-map@~0.1.7:
+ version "0.1.8"
+ resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-0.1.8.tgz#c550b2ab5427f6b3f21f5afead88c4f5587b2106"
+
source-list-map@^1.1.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-1.1.2.tgz#9889019d1024cce55cdc069498337ef6186a11a1"
-source-list-map@~0.1.7:
- version "0.1.8"
- resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-0.1.8.tgz#c550b2ab5427f6b3f21f5afead88c4f5587b2106"
-
source-map-support@^0.4.2:
version "0.4.15"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.15.tgz#03202df65c06d2bd8c7ec2362a193056fef8d3b1"
@@ -4585,6 +5413,14 @@ source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.0, sour
version "0.5.6"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
+spawn-rx@^2.0.10:
+ version "2.0.11"
+ resolved "https://registry.yarnpkg.com/spawn-rx/-/spawn-rx-2.0.11.tgz#65451ad65662801daea75549832a782de0048dbf"
+ dependencies:
+ debug "^2.5.1"
+ lodash.assign "^4.2.0"
+ rxjs "^5.1.1"
+
spdx-correct@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40"
@@ -4687,6 +5523,10 @@ stream-to-observable@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/stream-to-observable/-/stream-to-observable-0.1.0.tgz#45bf1d9f2d7dc09bed81f1c307c430e68b84cffe"
+strict-uri-encode@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
+
string-width@^1.0.1, string-width@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
@@ -4706,6 +5546,10 @@ string.prototype.codepointat@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/string.prototype.codepointat/-/string.prototype.codepointat-0.2.0.tgz#6b26e9bd3afcaa7be3b4269b526de1b82000ac78"
+string.prototype.repeat@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/string.prototype.repeat/-/string.prototype.repeat-0.2.0.tgz#aba36de08dcee6a5a337d49b2ea1da1b28fc0ecf"
+
string_decoder@^0.10.25, string_decoder@~0.10.x:
version "0.10.31"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
@@ -4750,16 +5594,41 @@ strip-json-comments@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
+style-loader@^0.18.2:
+ version "0.18.2"
+ resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.18.2.tgz#cc31459afbcd6d80b7220ee54b291a9fd66ff5eb"
+ dependencies:
+ loader-utils "^1.0.2"
+ schema-utils "^0.3.0"
+
supports-color@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
-supports-color@^3.1.0, supports-color@^3.1.1:
+supports-color@^3.1.0, supports-color@^3.1.1, supports-color@^3.2.3:
version "3.2.3"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6"
dependencies:
has-flag "^1.0.0"
+supports-color@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.0.0.tgz#33a7c680aa512c9d03ef929cacbb974d203d2790"
+ dependencies:
+ has-flag "^2.0.0"
+
+svgo@^0.7.0:
+ version "0.7.2"
+ resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5"
+ dependencies:
+ coa "~1.0.1"
+ colors "~1.1.2"
+ csso "~2.3.1"
+ js-yaml "~3.7.0"
+ mkdirp "~0.5.1"
+ sax "~1.2.1"
+ whet.extend "~0.9.9"
+
symbol-observable@^1.0.1, symbol-observable@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d"
@@ -4808,6 +5677,18 @@ text-table@~0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
+thenify-all@^1.0.0, thenify-all@^1.6.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726"
+ dependencies:
+ thenify ">= 3.1.0 < 4"
+
+"thenify@>= 3.1.0 < 4":
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.0.tgz#e69e38a1babe969b0108207978b9f62b88604839"
+ dependencies:
+ any-promise "^1.0.0"
+
through2@^0.6.2, through2@^0.6.5:
version "0.6.5"
resolved "https://registry.yarnpkg.com/through2/-/through2-0.6.5.tgz#41ab9c67b29d57209071410e1d7a7a968cd3ad48"
@@ -4886,6 +5767,10 @@ typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
+typo-js@*:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/typo-js/-/typo-js-1.0.3.tgz#54d8ebc7949f1a7810908b6002c6841526c99d5a"
+
ua-parser-js@^0.7.9:
version "0.7.13"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.13.tgz#cd9dd2f86493b3f44dbeeef3780fda74c5ee14be"
@@ -4920,6 +5805,20 @@ uint64be@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/uint64be/-/uint64be-1.0.1.tgz#1f7154202f2a1b8af353871dda651bf34ce93e95"
+uniq@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff"
+
+uniqid@^4.0.0:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/uniqid/-/uniqid-4.1.1.tgz#89220ddf6b751ae52b5f72484863528596bb84c1"
+ dependencies:
+ macaddress "^0.2.8"
+
+uniqs@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02"
+
unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
@@ -5002,6 +5901,10 @@ vary@~1.1.0, vary@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.1.tgz#67535ebb694c1d52257457984665323f587e8d37"
+vendors@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.1.tgz#37ad73c8ee417fb3d580e785312307d274847f22"
+
verror@1.3.6:
version "1.3.6"
resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c"
@@ -5172,6 +6075,14 @@ whatwg-fetch@>=0.10.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84"
+whatwg-fetch@^0.9.0:
+ version "0.9.0"
+ resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-0.9.0.tgz#0e3684c6cb9995b43efc9df03e4c365d95fd9cc0"
+
+whet.extend@~0.9.9:
+ version "0.9.9"
+ resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1"
+
which-module@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f"
@@ -5227,6 +6138,10 @@ write@^0.2.1:
dependencies:
mkdirp "^0.5.1"
+xss-filters@^1.2.6:
+ version "1.2.7"
+ resolved "https://registry.yarnpkg.com/xss-filters/-/xss-filters-1.2.7.tgz#59fa1de201f36f2f3470dcac5f58ccc2830b0a9a"
+
"xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
@@ -5269,7 +6184,7 @@ yargs@^6.0.0:
y18n "^3.2.1"
yargs-parser "^4.2.0"
-yargs@^7.0.0:
+yargs@^7.0.0, yargs@^7.0.2:
version "7.1.0"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8"
dependencies:
From b09d71ecffa1c4a43c074f526e667addc7739d3d Mon Sep 17 00:00:00 2001
From: Le Long
Date: Thu, 15 Jun 2017 21:30:56 +0200
Subject: [PATCH 05/26] Markdown Support
---
.gitignore | 1 +
CHANGELOG.md | 7 +-
ui/js/component/common.js | 2 +
ui/js/component/fileCard/view.jsx | 9 +-
ui/js/component/form.js | 24 ++-
ui/js/page/filePage/view.jsx | 7 +-
ui/js/page/publish/view.jsx | 272 ++++++++++++++++++-----------
ui/package.json | 5 +
ui/scss/component/_form-field.scss | 7 +
9 files changed, 224 insertions(+), 110 deletions(-)
diff --git a/.gitignore b/.gitignore
index c212707ad..233924d55 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,3 +26,4 @@ build/daemon.zip
.vimrc
package-lock.json
+ui/yarn.lock
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bcbe49bfb..d8f65e2ee 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,14 +10,17 @@ Web UI version numbers should always match the corresponding version of LBRY App
### Added
* Added option to release claim when deleting a file
* Added transition to card hovers to smooth animation
+ * Support markdown makeup in claim description
+ *
### Changed
- *
+ * Publishes now uses claims rather than files
*
### Fixed
* Fixed bug with download notice when switching window focus
- *
+ * Fixed newly published files appearing twice
+ * Fixed unconfirmed published files missing channel name
### Deprecated
*
diff --git a/ui/js/component/common.js b/ui/js/component/common.js
index 1b48bc4df..57314bdd3 100644
--- a/ui/js/component/common.js
+++ b/ui/js/component/common.js
@@ -1,5 +1,7 @@
import React from "react";
+import ReactDOMServer from "react-dom/server";
import lbry from "../lbry.js";
+import ReactMarkdown from "react-markdown";
//component/icon.js
export class Icon extends React.PureComponent {
diff --git a/ui/js/component/fileCard/view.jsx b/ui/js/component/fileCard/view.jsx
index 42c0e5b68..256bc9014 100644
--- a/ui/js/component/fileCard/view.jsx
+++ b/ui/js/component/fileCard/view.jsx
@@ -1,7 +1,12 @@
import React from "react";
import lbryuri from "lbryuri.js";
import Link from "component/link";
-import { TruncatedText, Icon } from "component/common";
+import {
+ Thumbnail,
+ TruncatedText,
+ Icon,
+ TruncatedMarkdown,
+} from "component/common";
import FilePrice from "component/filePrice";
import UriIndicator from "component/uriIndicator";
import NsfwOverlay from "component/nsfwOverlay";
@@ -94,7 +99,7 @@ class FileCard extends React.PureComponent {
style={{ backgroundImage: "url('" + metadata.thumbnail + "')" }}
/>}
- {description}
+ {description}
diff --git a/ui/js/component/form.js b/ui/js/component/form.js
index 7ab78325c..6a65218e4 100644
--- a/ui/js/component/form.js
+++ b/ui/js/component/form.js
@@ -1,8 +1,9 @@
import React from "react";
import FileSelector from "./file-selector.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,
+let formFieldCounter = 0,
formFieldFileSelectorTypes = ["file", "directory"],
formFieldNestedLabelTypes = ["radio", "checkbox"];
@@ -24,6 +25,7 @@ export class FormField extends React.PureComponent {
this._fieldRequiredText = __("This field is required");
this._type = null;
this._element = null;
+ this._extraElementProps = {};
this.state = {
isError: null,
@@ -38,6 +40,12 @@ export class FormField extends React.PureComponent {
} else if (this.props.type == "text-number") {
this._element = "input";
this._type = "text";
+ } else if (this.props.type == "SimpleMDE") {
+ this._element = SimpleMDE;
+ this._type = "textarea";
+ this._extraElementProps.options = {
+ hideIcons: ["guide", "heading", "image", "fullscreen"],
+ };
} else if (formFieldFileSelectorTypes.includes(this.props.type)) {
this._element = "input";
this._type = "hidden";
@@ -81,6 +89,8 @@ export class FormField extends React.PureComponent {
getValue() {
if (this.props.type == "checkbox") {
return this.refs.field.checked;
+ } else if (this.props.type == "SimpleMDE") {
+ return this.refs.field.simplemde.value();
} else {
return this.refs.field.value;
}
@@ -90,6 +100,10 @@ export class FormField extends React.PureComponent {
return this.refs.field.options[this.refs.field.selectedIndex];
}
+ getOptions() {
+ return this.refs.field.options;
+ }
+
render() {
// Pass all unhandled props to the field element
const otherProps = Object.assign({}, this.props),
@@ -106,7 +120,6 @@ export class FormField extends React.PureComponent {
delete otherProps.className;
delete otherProps.postfix;
delete otherProps.prefix;
-
const element = (
{this.props.children}
@@ -220,6 +234,10 @@ export class FormRow extends React.PureComponent {
return this.refs.field.getSelectedElement();
}
+ getOptions() {
+ return this.refs.field.getOptions();
+ }
+
focus() {
this.refs.field.focus();
}
diff --git a/ui/js/page/filePage/view.jsx b/ui/js/page/filePage/view.jsx
index adb478bdc..82ac64c57 100644
--- a/ui/js/page/filePage/view.jsx
+++ b/ui/js/page/filePage/view.jsx
@@ -1,4 +1,5 @@
import React from "react";
+import ReactMarkdown from "react-markdown";
import lbry from "lbry.js";
import lbryuri from "lbryuri.js";
import Video from "component/video";
@@ -119,7 +120,11 @@ class FilePage extends React.PureComponent {
- {metadata && metadata.description}
+
{metadata
diff --git a/ui/js/page/publish/view.jsx b/ui/js/page/publish/view.jsx
index 12b38cbd7..b76d018a1 100644
--- a/ui/js/page/publish/view.jsx
+++ b/ui/js/page/publish/view.jsx
@@ -5,13 +5,16 @@ import { FormField, FormRow } from "component/form.js";
import Link from "component/link";
import rewards from "rewards";
import Modal from "component/modal";
+import Notice from "component/notice";
import { BusyMessage } from "component/common";
class PublishPage extends React.PureComponent {
constructor(props) {
super(props);
- this._requiredFields = ["meta_title", "name", "bid", "tos_agree"];
+ this._requiredFields = ["name", "bid", "meta_title", "tosAgree"];
+
+ this._defaultCopyrightNotice = "All rights reserved.";
this.state = {
rawName: "",
@@ -23,11 +26,17 @@ class PublishPage extends React.PureComponent {
channel: "anonymous",
newChannelName: "@",
newChannelBid: 10,
- myClaimValue: 0.0,
- myClaimMetadata: null,
- copyrightNotice: "",
+ meta_title: "",
+ meta_thumbnail: "",
+ meta_description: "",
+ meta_language: "en",
+ meta_nsfw: "0",
+ licenseType: "",
+ copyrightNotice: this._defaultCopyrightNotice,
otherLicenseDescription: "",
otherLicenseUrl: "",
+ tosAgree: false,
+ prefillDone: false,
uploadProgress: 0.0,
uploaded: false,
errorMessage: null,
@@ -80,36 +89,18 @@ class PublishPage extends React.PureComponent {
return;
}
- if (this.state.nameIsMine) {
- // Pre-populate with existing metadata
- var metadata = Object.assign({}, this.state.myClaimMetadata);
- if (this.refs.file.getValue() !== "") {
- delete metadata.sources;
- }
- } else {
- var metadata = {};
- }
+ let metadata = {};
- for (let metaField of [
- "title",
- "description",
- "thumbnail",
- "license",
- "license_url",
- "language",
- ]) {
- var value = this.refs["meta_" + metaField].getValue();
- if (value !== "") {
+ for (let metaField of ["title", "description", "thumbnail", "language"]) {
+ const value = this.state["meta_" + metaField];
+ if (value) {
metadata[metaField] = value;
}
}
- metadata.nsfw = parseInt(this.refs.meta_nsfw.getValue()) === 1;
-
- const licenseUrl = this.refs.meta_license_url.getValue();
- if (licenseUrl) {
- metadata.license_url = licenseUrl;
- }
+ metadata.license = this.getLicense();
+ metadata.licenseUrl = this.getLicenseUrl();
+ metadata.nsfw = !!parseInt(this.state.meta_nsfw);
var doPublish = () => {
var publishArgs = {
@@ -203,6 +194,8 @@ class PublishPage extends React.PureComponent {
}
myClaimInfo() {
+ const { name } = this.state;
+
return Object.values(this.props.myClaims).find(
claim => claim.name === name
);
@@ -240,6 +233,7 @@ class PublishPage extends React.PureComponent {
this.setState({
rawName: rawName,
name: name,
+ prefillDone: false,
uri,
});
@@ -254,6 +248,43 @@ class PublishPage extends React.PureComponent {
});
}
+ handlePrefillClicked() {
+ const {license, licenseUrl, title, thumbnail, description,
+ language, nsfw} = this.myClaimInfo().value.stream.metadata;
+
+ let newState = {
+ meta_title: title,
+ meta_thumbnail: thumbnail,
+ meta_description: description,
+ meta_language: language,
+ meta_nsfw: nsfw,
+ };
+
+ if (license == this._defaultCopyrightNotice) {
+ newState.licenseType = "copyright";
+ newState.copyrightNotice = this._defaultCopyrightNotice;
+ } else {
+ // If the license URL or description matches one of the drop-down options, use that
+ let licenseType = "other"; // Will be overridden if we find a match
+ for (let option of this._meta_license.getOptions()) {
+ if (
+ option.getAttribute("data-url") === licenseUrl ||
+ option.text === license
+ ) {
+ licenseType = option.value;
+ }
+ }
+
+ if (licenseType == "other") {
+ newState.otherLicenseDescription = license;
+ newState.otherLicenseUrl = licenseUrl;
+ }
+ newState.licenseType = licenseType;
+ }
+
+ this.setState(newState);
+ }
+
handleBidChange(event) {
this.setState({
bid: event.target.value,
@@ -278,20 +309,21 @@ class PublishPage extends React.PureComponent {
});
}
- handleLicenseChange(event) {
- var licenseType = event.target.options[
- event.target.selectedIndex
- ].getAttribute("data-license-type");
- var newState = {
- copyrightChosen: licenseType == "copyright",
- otherLicenseChosen: licenseType == "other",
- };
+ handleMetadataChange(event) {
+ /**
+ * This function is used for all metadata inputs that store the final value directly into state.
+ * The only exceptions are inputs related to license description and license URL, which require
+ * more complex logic and the final value is determined at submit time.
+ */
+ this.setState({
+ ["meta_" + event.target.name]: event.target.value,
+ });
+ }
- if (licenseType == "copyright") {
- newState.copyrightNotice = __("All rights reserved.");
- }
-
- this.setState(newState);
+ handleLicenseTypeChange(event) {
+ this.setState({
+ licenseType: event.target.value,
+ });
}
handleCopyrightNoticeChange(event) {
@@ -322,7 +354,7 @@ class PublishPage extends React.PureComponent {
handleTOSChange(event) {
this.setState({
- TOSAgreed: event.target.checked,
+ tosAgree: event.target.checked,
});
}
@@ -366,16 +398,25 @@ class PublishPage extends React.PureComponent {
);
}
+ getLicense() {
+ switch (this.state.licenseType) {
+ case "copyright":
+ return this.state.copyrightNotice;
+ case "other":
+ return this.state.otherLicenseDescription;
+ default:
+ return this._meta_license.getSelectedElement().text;
+ }
+ }
+
getLicenseUrl() {
- if (!this.refs.meta_license) {
- return "";
- } else if (this.state.otherLicenseChosen) {
- return this.state.otherLicenseUrl;
- } else {
- return (
- this.refs.meta_license.getSelectedElement().getAttribute("data-url") ||
- ""
- );
+ switch (this.state.licenseType) {
+ case "copyright":
+ return "";
+ case "other":
+ return this.state.otherLicenseUrl;
+ default:
+ return this._meta_license.getSelectedElement().getAttribute("data-url");
}
}
@@ -398,7 +439,7 @@ class PublishPage extends React.PureComponent {
this.props.resolvingUris.indexOf(this.state.uri) !== -1 &&
this.claim() === undefined
) {
- return ;
+ return __("Checking...");
} else if (!this.state.name) {
return __("Select a URL for this publish.");
} else if (!this.claim()) {
@@ -482,43 +523,55 @@ class PublishPage extends React.PureComponent {
}
/>
- {!this.state.hasFile
- ? ""
+ {!this.state.hasFile && !this.myClaimExists()
+ ? null
:
{
+ this.handleMetadataChange(event);
+ }}
/>
{
+ this.handleMetadataChange(event);
+ }}
/>
{
+ this.handleMetadataChange(event);
+ }}
/>
{
+ this.handleMetadataChange(event);
+ }}
>
{__("English")}
{__("Chinese")}
@@ -533,9 +586,11 @@ class PublishPage extends React.PureComponent {
{
+ this.handleMetadataChange(event);
+ }}
>
{/* */}
{__("All Ages")}
@@ -583,8 +638,7 @@ class PublishPage extends React.PureComponent {
placeholder="1.00"
min="0.01"
onChange={event => this.handleFeeAmountChange(event)}
- />
- {" "}
+ />{" "}
{
@@ -605,66 +659,71 @@ class PublishPage extends React.PureComponent {
{
+ this._meta_license = row;
+ }}
onChange={event => {
- this.handleLicenseChange(event);
+ this.handleLicenseTypeChange(event);
}}
>
- {__("Public Domain")}
-
+ {__("Public Domain")}
+
{__("Creative Commons Attribution 4.0 International")}
-
+
{__(
"Creative Commons Attribution-ShareAlike 4.0 International"
)}
-
+
{__(
"Creative Commons Attribution-NoDerivatives 4.0 International"
)}
-
+
{__(
"Creative Commons Attribution-NonCommercial 4.0 International"
)}
-
+
{__(
"Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International"
)}
-
+
{__(
"Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International"
)}
-
+
{__("Copyrighted...")}
-
+
{__("Other...")}
-
- {this.state.copyrightChosen
+
+ {this.state.licenseType == "copyright"
?
: null}
- {this.state.otherLicenseChosen
+
+ {this.state.licenseType == "other"
? {
- this.handleOtherLicenseDescriptionChange();
+ this.handleOtherLicenseDescriptionChange(event);
}}
/>
: null}
- {this.state.otherLicenseChosen
+
+ {this.state.licenseType == "other"
? {
this.handleOtherLicenseUrlChange(event);
}}
@@ -730,6 +793,15 @@ class PublishPage extends React.PureComponent {
}}
helper={this.getNameBidHelpText()}
/>
+ {this.myClaimExists() && !this.state.prefillDone
+ ?
+ {__("You already have a claim with this name.")}{" "}
+ this.handlePrefillClicked()}
+ />
+
+ : null}
{this.state.rawName
?
@@ -763,15 +835,11 @@ class PublishPage extends React.PureComponent {
}
type="checkbox"
- name="tos_agree"
- ref={field => {
- this.refs.tos_agree = field;
- }}
+ checked={this.state.tosAgree}
onChange={event => {
this.handleTOSChange(event);
}}
diff --git a/ui/package.json b/ui/package.json
index d7aa3535a..4c4986ec2 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -29,8 +29,10 @@
"rc-progress": "^2.0.6",
"react": "^15.4.0",
"react-dom": "^15.4.0",
+ "react-markdown": "^2.5.0",
"react-modal": "^1.5.2",
"react-redux": "^5.0.3",
+ "react-simplemde-editor": "^3.6.11",
"redux": "^3.6.0",
"redux-action-buffer": "^1.1.0",
"redux-logger": "^3.0.1",
@@ -52,6 +54,8 @@
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-2": "^6.18.0",
+ "electron-rebuild": "^1.5.11",
+ "css-loader": "^0.28.4",
"eslint": "^3.10.2",
"eslint-config-airbnb": "^13.0.0",
"eslint-loader": "^1.6.1",
@@ -64,6 +68,7 @@
"lint-staged": "^3.6.0",
"node-loader": "^0.6.0",
"prettier": "^1.4.2",
+ "style-loader": "^0.18.2",
"webpack": "^2.6.1",
"webpack-dev-server": "^2.4.4",
"webpack-notifier": "^1.5.0",
diff --git a/ui/scss/component/_form-field.scss b/ui/scss/component/_form-field.scss
index 8fd86efef..f701ebe06 100644
--- a/ui/scss/component/_form-field.scss
+++ b/ui/scss/component/_form-field.scss
@@ -117,6 +117,9 @@ input[type="text"].input-copyable {
border: $width-input-border solid $color-form-border;
}
}
+.form-field--SimpleMDE {
+ display: block;
+}
.form-field__label {
&[for] { cursor: pointer; }
@@ -163,4 +166,8 @@ input[type="text"].input-copyable {
}
.form-field__helper {
color: $color-help;
+}
+
+.form-field__input.form-field__input-SimpleMDE .CodeMirror-scroll {
+ height: auto;
}
\ No newline at end of file
From 7c5187c4e4d8e1cef4d6a478f12a475fcf47adcf Mon Sep 17 00:00:00 2001
From: 6ea86b96 <6ea86b96@gmail.com>
Date: Fri, 30 Jun 2017 15:45:54 +0700
Subject: [PATCH 06/26] Create publish form component, fix markdown editor, fix
prefill
---
ui/js/component/publishForm/index.js | 5 +
.../publishForm/internal/channelSection.jsx | 179 +++
ui/js/component/publishForm/view.jsx | 920 ++++++++++++++
ui/js/page/publish/view.jsx | 1076 +----------------
4 files changed, 1108 insertions(+), 1072 deletions(-)
create mode 100644 ui/js/component/publishForm/index.js
create mode 100644 ui/js/component/publishForm/internal/channelSection.jsx
create mode 100644 ui/js/component/publishForm/view.jsx
diff --git a/ui/js/component/publishForm/index.js b/ui/js/component/publishForm/index.js
new file mode 100644
index 000000000..3e2d02b42
--- /dev/null
+++ b/ui/js/component/publishForm/index.js
@@ -0,0 +1,5 @@
+import React from "react";
+import { connect } from "react-redux";
+import PublishForm from "./view";
+
+export default connect()(PublishForm);
diff --git a/ui/js/component/publishForm/internal/channelSection.jsx b/ui/js/component/publishForm/internal/channelSection.jsx
new file mode 100644
index 000000000..c0c4bf473
--- /dev/null
+++ b/ui/js/component/publishForm/internal/channelSection.jsx
@@ -0,0 +1,179 @@
+import React from "react";
+import lbryuri from "lbryuri";
+import { FormField, FormRow } from "component/form.js";
+import { BusyMessage } from "component/common";
+
+class ChannelSection extends React.PureComponent {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ newChannelName: "@",
+ newChannelBid: 10,
+ addingChannel: false,
+ };
+ }
+
+ handleChannelChange(event) {
+ const channel = event.target.value;
+ if (channel === "new") this.setState({ addingChannel: true });
+ else {
+ this.setState({ addingChannel: false });
+ this.props.handleChannelChange(event.target.value);
+ }
+ }
+
+ handleNewChannelNameChange(event) {
+ const newChannelName = event.target.value.startsWith("@")
+ ? event.target.value
+ : "@" + event.target.value;
+
+ if (
+ newChannelName.length > 1 &&
+ !lbryuri.isValidName(newChannelName.substr(1), false)
+ ) {
+ this.refs.newChannelName.showError(
+ __("LBRY channel names must contain only letters, numbers and dashes.")
+ );
+ return;
+ } else {
+ this.refs.newChannelName.clearError();
+ }
+
+ this.setState({
+ newChannelName,
+ });
+ }
+
+ handleNewChannelBidChange(event) {
+ this.setState({
+ newChannelBid: event.target.value,
+ });
+ }
+
+ handleCreateChannelClick(event) {
+ if (this.state.newChannelName.length < 5) {
+ this.refs.newChannelName.showError(
+ __("LBRY channel names must be at least 4 characters in length.")
+ );
+ return;
+ }
+
+ this.setState({
+ creatingChannel: true,
+ });
+
+ const newChannelName = this.state.newChannelName;
+ const amount = parseFloat(this.state.newChannelBid);
+ this.setState({
+ creatingChannel: true,
+ });
+ const success = (() => {
+ this.setState({
+ creatingChannel: false,
+ addingChannel: false,
+ channel: newChannelName,
+ });
+ this.props.handleChannelChange(newChannelName);
+ }).bind(this);
+ const failure = (err => {
+ this.setState({
+ creatingChannel: false,
+ });
+ this.refs.newChannelName.showError(
+ __("Unable to create channel due to an internal error.")
+ );
+ }).bind(this);
+ this.props.createChannel(newChannelName, amount).then(success, failure);
+ }
+
+ render() {
+ const lbcInputHelp = __(
+ "This LBC remains yours and the deposit can be undone at any time."
+ );
+
+ const { fetchingChannels, channels } = this.props;
+
+ let channelContent = [];
+ if (channels.length > 0) {
+ channelContent.push(
+
+
+ {__("Anonymous")}
+
+ {this.props.channels.map(({ name }) =>
+ {name}
+ )}
+
+ {__("New identity...")}
+
+
+ );
+ if (fetchingChannels) {
+ channelContent.push(
+
+ );
+ }
+ } else if (fetchingChannels) {
+ channelContent.push(
+
+ );
+ }
+
+ return (
+
+
+
{__("Identity")}
+
+ {__("Who created this content?")}
+
+
+
+ {channelContent}
+
+ {this.state.addingChannel &&
+
+
{
+ this.handleNewChannelNameChange(event);
+ }}
+ value={this.state.newChannelName}
+ />
+
+
+
+
+ }
+
+ );
+ }
+}
+
+export default ChannelSection;
diff --git a/ui/js/component/publishForm/view.jsx b/ui/js/component/publishForm/view.jsx
new file mode 100644
index 000000000..91fd5eb09
--- /dev/null
+++ b/ui/js/component/publishForm/view.jsx
@@ -0,0 +1,920 @@
+import React from "react";
+import lbry from "lbry";
+import lbryuri from "lbryuri";
+import { FormField, FormRow } from "component/form.js";
+import Link from "component/link";
+import Modal from "component/modal";
+import Notice from "component/notice";
+import { BusyMessage } from "component/common";
+import ChannelSection from "./internal/ChannelSection";
+
+class PublishForm extends React.PureComponent {
+ constructor(props) {
+ super(props);
+
+ this._requiredFields = ["name", "bid", "meta_title", "tosAgree"];
+
+ this._defaultCopyrightNotice = "All rights reserved.";
+
+ this.state = {
+ rawName: "",
+ name: "",
+ bid: 10,
+ hasFile: false,
+ feeAmount: "",
+ feeCurrency: "USD",
+ channel: "anonymous",
+ newChannelName: "@",
+ newChannelBid: 10,
+ meta_title: "",
+ meta_thumbnail: "",
+ meta_description: "",
+ meta_language: "en",
+ meta_nsfw: "0",
+ licenseType: "",
+ copyrightNotice: this._defaultCopyrightNotice,
+ otherLicenseDescription: "",
+ otherLicenseUrl: "",
+ tosAgree: false,
+ prefillDone: false,
+ uploadProgress: 0.0,
+ uploaded: false,
+ errorMessage: null,
+ submitting: false,
+ creatingChannel: false,
+ modal: null,
+ };
+ }
+
+ _updateChannelList(channel) {
+ const { fetchingChannels, fetchChannelListMine } = this.props;
+
+ if (!fetchingChannels) fetchChannelListMine();
+ }
+
+ handleSubmit(event) {
+ if (typeof event !== "undefined") {
+ event.preventDefault();
+ }
+
+ this.setState({
+ submitting: true,
+ });
+
+ let checkFields = this._requiredFields;
+ if (!this.myClaimExists()) {
+ checkFields.unshift("file");
+ }
+
+ let missingFieldFound = false;
+ for (let fieldName of checkFields) {
+ const field = this.refs[fieldName];
+ if (field) {
+ if (field.getValue() === "" || field.getValue() === false) {
+ field.showRequiredError();
+ if (!missingFieldFound) {
+ field.focus();
+ missingFieldFound = true;
+ }
+ } else {
+ field.clearError();
+ }
+ }
+ }
+
+ if (missingFieldFound) {
+ this.setState({
+ submitting: false,
+ });
+ return;
+ }
+
+ let metadata = {};
+
+ for (let metaField of ["title", "description", "thumbnail", "language"]) {
+ const value = this.state["meta_" + metaField];
+ if (value) {
+ metadata[metaField] = value;
+ }
+ }
+
+ metadata.license = this.getLicense();
+ metadata.licenseUrl = this.getLicenseUrl();
+ metadata.nsfw = !!parseInt(this.state.meta_nsfw);
+
+ var doPublish = () => {
+ var publishArgs = {
+ name: this.state.name,
+ bid: parseFloat(this.state.bid),
+ metadata: metadata,
+ ...(this.state.channel != "new" && this.state.channel != "anonymous"
+ ? { channel_name: this.state.channel }
+ : {}),
+ };
+
+ if (this.refs.file.getValue() !== "") {
+ publishArgs.file_path = this.refs.file.getValue();
+ }
+
+ const success = claim => {};
+ const failure = error => this.handlePublishError(error);
+
+ this.handlePublishStarted();
+ this.props.publish(publishArgs).then(success, failure);
+ };
+
+ if (this.state.isFee) {
+ lbry.wallet_unused_address().then(address => {
+ metadata.fee = {
+ currency: this.state.feeCurrency,
+ amount: parseFloat(this.state.feeAmount),
+ address: address,
+ };
+
+ doPublish();
+ });
+ } else {
+ doPublish();
+ }
+ }
+
+ handlePublishStarted() {
+ this.setState({
+ modal: "publishStarted",
+ });
+ }
+
+ handlePublishStartedConfirmed() {
+ this.props.navigate("/published");
+ }
+
+ handlePublishError(error) {
+ this.setState({
+ submitting: false,
+ modal: "error",
+ errorMessage: error.message,
+ });
+ }
+
+ claim() {
+ const { claimsByUri } = this.props;
+ const { uri } = this.state;
+
+ return claimsByUri[uri];
+ }
+
+ topClaimValue() {
+ if (!this.claim()) return null;
+
+ return parseFloat(this.claim().amount);
+ }
+
+ myClaimExists() {
+ const { myClaims } = this.props;
+ const { name } = this.state;
+
+ if (!name) return false;
+
+ return !!myClaims.find(claim => claim.name === name);
+ }
+
+ topClaimIsMine() {
+ const myClaimInfo = this.myClaimInfo();
+ const { claimsByUri } = this.props;
+ const { uri } = this.state;
+
+ if (!uri) return null;
+
+ const claim = claimsByUri[uri];
+
+ if (!claim) return true;
+ if (!myClaimInfo) return false;
+
+ return myClaimInfo.amount >= claimInfo.amount;
+ }
+
+ myClaimInfo() {
+ const { name } = this.state;
+
+ return Object.values(this.props.myClaims).find(
+ claim => claim.name === name
+ );
+ }
+
+ handleNameChange(event) {
+ var rawName = event.target.value;
+
+ this.nameChanged(rawName);
+ }
+
+ nameChanged(rawName) {
+ if (!rawName) {
+ this.setState({
+ rawName: "",
+ name: "",
+ uri: "",
+ prefillDone: false,
+ });
+
+ return;
+ }
+
+ if (!lbryuri.isValidName(rawName, false)) {
+ this.refs.name.showError(
+ __("LBRY names must contain only letters, numbers and dashes.")
+ );
+ return;
+ }
+
+ let channel = "";
+ if (this.state.channel !== "anonymous") channel = this.state.channel;
+
+ const name = rawName.toLowerCase();
+ const uri = lbryuri.build({ contentName: name, channelName: channel });
+ this.setState({
+ rawName: rawName,
+ name: name,
+ prefillDone: false,
+ uri,
+ });
+
+ if (this.resolveUriTimeout) {
+ clearTimeout(this.resolveUriTimeout);
+ this.resolveUriTimeout = undefined;
+ }
+ const resolve = () => this.props.resolveUri(uri);
+
+ this.resolveUriTimeout = setTimeout(resolve.bind(this), 500, {
+ once: true,
+ });
+ }
+
+ handlePrefillClicked() {
+ const claimInfo = this.myClaimInfo();
+ const {
+ license,
+ licenseUrl,
+ title,
+ thumbnail,
+ description,
+ language,
+ nsfw,
+ } = claimInfo.value.stream.metadata;
+
+ let newState = {
+ meta_title: title,
+ meta_thumbnail: thumbnail,
+ meta_description: description,
+ meta_language: language,
+ meta_nsfw: nsfw,
+ prefillDone: true,
+ bid: claimInfo.amount,
+ };
+
+ if (license == this._defaultCopyrightNotice) {
+ newState.licenseType = "copyright";
+ newState.copyrightNotice = this._defaultCopyrightNotice;
+ } else {
+ // If the license URL or description matches one of the drop-down options, use that
+ let licenseType = "other"; // Will be overridden if we find a match
+ for (let option of this._meta_license.getOptions()) {
+ if (
+ option.getAttribute("data-url") === licenseUrl ||
+ option.text === license
+ ) {
+ licenseType = option.value;
+ }
+ }
+
+ if (licenseType == "other") {
+ newState.otherLicenseDescription = license;
+ newState.otherLicenseUrl = licenseUrl;
+ }
+ newState.licenseType = licenseType;
+ }
+
+ console.log(newState);
+ this.setState(newState);
+ }
+
+ handleBidChange(event) {
+ this.setState({
+ bid: event.target.value,
+ });
+ }
+
+ handleFeeAmountChange(event) {
+ this.setState({
+ feeAmount: event.target.value,
+ });
+ }
+
+ handleFeeCurrencyChange(event) {
+ this.setState({
+ feeCurrency: event.target.value,
+ });
+ }
+
+ handleFeePrefChange(feeEnabled) {
+ this.setState({
+ isFee: feeEnabled,
+ });
+ }
+
+ handleMetadataChange(event) {
+ /**
+ * This function is used for all metadata inputs that store the final value directly into state.
+ * The only exceptions are inputs related to license description and license URL, which require
+ * more complex logic and the final value is determined at submit time.
+ */
+ this.setState({
+ ["meta_" + event.target.name]: event.target.value,
+ });
+ }
+
+ handleDescriptionChanged(text) {
+ this.setState({
+ meta_description: text,
+ });
+ }
+
+ handleLicenseTypeChange(event) {
+ this.setState({
+ licenseType: event.target.value,
+ });
+ }
+
+ handleCopyrightNoticeChange(event) {
+ this.setState({
+ copyrightNotice: event.target.value,
+ });
+ }
+
+ handleOtherLicenseDescriptionChange(event) {
+ this.setState({
+ otherLicenseDescription: event.target.value,
+ });
+ }
+
+ handleOtherLicenseUrlChange(event) {
+ this.setState({
+ otherLicenseUrl: event.target.value,
+ });
+ }
+
+ handleChannelChange(channelName) {
+ this.setState({
+ channel: channelName,
+ });
+ const nameChanged = () => this.nameChanged(this.state.rawName);
+ setTimeout(nameChanged.bind(this), 500, { once: true });
+ }
+
+ handleTOSChange(event) {
+ this.setState({
+ tosAgree: event.target.checked,
+ });
+ }
+
+ handleCreateChannelClick(event) {
+ if (this.state.newChannelName.length < 5) {
+ this.refs.newChannelName.showError(
+ __("LBRY channel names must be at least 4 characters in length.")
+ );
+ return;
+ }
+
+ this.setState({
+ creatingChannel: true,
+ });
+
+ const newChannelName = this.state.newChannelName;
+ lbry
+ .channel_new({
+ channel_name: newChannelName,
+ amount: parseFloat(this.state.newChannelBid),
+ })
+ .then(
+ () => {
+ setTimeout(() => {
+ this.setState({
+ creatingChannel: false,
+ });
+
+ this._updateChannelList(newChannelName);
+ }, 10000);
+ },
+ error => {
+ // TODO: better error handling
+ this.refs.newChannelName.showError(
+ __("Unable to create channel due to an internal error.")
+ );
+ this.setState({
+ creatingChannel: false,
+ });
+ }
+ );
+ }
+
+ getLicense() {
+ switch (this.state.licenseType) {
+ case "copyright":
+ return this.state.copyrightNotice;
+ case "other":
+ return this.state.otherLicenseDescription;
+ default:
+ return this._meta_license.getSelectedElement().text;
+ }
+ }
+
+ getLicenseUrl() {
+ switch (this.state.licenseType) {
+ case "copyright":
+ return "";
+ case "other":
+ return this.state.otherLicenseUrl;
+ default:
+ return this._meta_license.getSelectedElement().getAttribute("data-url");
+ }
+ }
+
+ componentWillMount() {
+ this.props.fetchClaimListMine();
+ this._updateChannelList();
+ }
+
+ onFileChange() {
+ if (this.refs.file.getValue()) {
+ this.setState({ hasFile: true });
+ } else {
+ this.setState({ hasFile: false });
+ }
+ }
+
+ getNameBidHelpText() {
+ if (this.state.prefillDone) {
+ return (
+
+ {__("Existing claim data was prefilled")}
+
+ );
+ }
+
+ if (
+ this.state.uri &&
+ this.props.resolvingUris.indexOf(this.state.uri) !== -1 &&
+ this.claim() === undefined
+ ) {
+ return __("Checking...");
+ } else if (!this.state.name) {
+ return __("Select a URL for this publish.");
+ } else if (!this.claim()) {
+ return __("This URL is unused.");
+ } else if (this.myClaimExists() && !this.state.prefillDone) {
+ return (
+
+ {__("You already have a claim with this name.")}{" "}
+ this.handlePrefillClicked()}
+ />
+
+ );
+ } else if (this.claim()) {
+ if (this.topClaimValue() === 1) {
+ return (
+
+ {__(
+ 'A deposit of at least one credit is required to win "%s". However, you can still get a permanent URL for any amount.',
+ this.state.name
+ )}
+
+ );
+ } else {
+ return (
+
+ {__(
+ 'A deposit of at least "%s" credits is required to win "%s". However, you can still get a permanent URL for any amount.',
+ this.topClaimValue(),
+ this.state.name
+ )}
+
+ );
+ }
+ } else {
+ return "";
+ }
+ }
+
+ closeModal() {
+ this.setState({
+ modal: null,
+ });
+ }
+
+ render() {
+ const lbcInputHelp = __(
+ "This LBC remains yours and the deposit can be undone at any time."
+ );
+
+ return (
+
+
+
+ {
+ this.handlePublishStartedConfirmed(event);
+ }}
+ >
+
+ {__("Your file has been published to LBRY at the address")}
+ {" "}{this.state.uri}
!
+
+
+ {__(
+ 'The file will take a few minutes to appear for other LBRY users. Until then it will be listed as "pending" under your published files.'
+ )}
+
+
+ {
+ this.closeModal(event);
+ }}
+ >
+ {__(
+ "The following error occurred when attempting to publish your file"
+ )}: {this.state.errorMessage}
+
+
+ );
+ }
+}
+
+export default PublishForm;
diff --git a/ui/js/page/publish/view.jsx b/ui/js/page/publish/view.jsx
index b76d018a1..ab39fcec8 100644
--- a/ui/js/page/publish/view.jsx
+++ b/ui/js/page/publish/view.jsx
@@ -1,1076 +1,8 @@
import React from "react";
-import lbry from "lbry";
-import lbryuri from "lbryuri";
-import { FormField, FormRow } from "component/form.js";
-import Link from "component/link";
-import rewards from "rewards";
-import Modal from "component/modal";
-import Notice from "component/notice";
-import { BusyMessage } from "component/common";
+import PublishForm from "component/publishForm";
-class PublishPage extends React.PureComponent {
- constructor(props) {
- super(props);
-
- this._requiredFields = ["name", "bid", "meta_title", "tosAgree"];
-
- this._defaultCopyrightNotice = "All rights reserved.";
-
- this.state = {
- rawName: "",
- name: "",
- bid: 10,
- hasFile: false,
- feeAmount: "",
- feeCurrency: "USD",
- channel: "anonymous",
- newChannelName: "@",
- newChannelBid: 10,
- meta_title: "",
- meta_thumbnail: "",
- meta_description: "",
- meta_language: "en",
- meta_nsfw: "0",
- licenseType: "",
- copyrightNotice: this._defaultCopyrightNotice,
- otherLicenseDescription: "",
- otherLicenseUrl: "",
- tosAgree: false,
- prefillDone: false,
- uploadProgress: 0.0,
- uploaded: false,
- errorMessage: null,
- submitting: false,
- creatingChannel: false,
- modal: null,
- };
- }
-
- _updateChannelList(channel) {
- const { fetchingChannels, fetchChannelListMine } = this.props;
-
- if (!fetchingChannels) fetchChannelListMine();
- }
-
- handleSubmit(event) {
- if (typeof event !== "undefined") {
- event.preventDefault();
- }
-
- this.setState({
- submitting: true,
- });
-
- let checkFields = this._requiredFields;
- if (!this.myClaimExists()) {
- checkFields.unshift("file");
- }
-
- let missingFieldFound = false;
- for (let fieldName of checkFields) {
- const field = this.refs[fieldName];
- if (field) {
- if (field.getValue() === "" || field.getValue() === false) {
- field.showRequiredError();
- if (!missingFieldFound) {
- field.focus();
- missingFieldFound = true;
- }
- } else {
- field.clearError();
- }
- }
- }
-
- if (missingFieldFound) {
- this.setState({
- submitting: false,
- });
- return;
- }
-
- let metadata = {};
-
- for (let metaField of ["title", "description", "thumbnail", "language"]) {
- const value = this.state["meta_" + metaField];
- if (value) {
- metadata[metaField] = value;
- }
- }
-
- metadata.license = this.getLicense();
- metadata.licenseUrl = this.getLicenseUrl();
- metadata.nsfw = !!parseInt(this.state.meta_nsfw);
-
- var doPublish = () => {
- var publishArgs = {
- name: this.state.name,
- bid: parseFloat(this.state.bid),
- metadata: metadata,
- ...(this.state.channel != "new" && this.state.channel != "anonymous"
- ? { channel_name: this.state.channel }
- : {}),
- };
-
- if (this.refs.file.getValue() !== "") {
- publishArgs.file_path = this.refs.file.getValue();
- }
-
- const success = claim => {};
- const failure = error => this.handlePublishError(error);
-
- this.handlePublishStarted();
- this.props.publish(publishArgs).then(success, failure);
- };
-
- if (this.state.isFee) {
- lbry.wallet_unused_address().then(address => {
- metadata.fee = {
- currency: this.state.feeCurrency,
- amount: parseFloat(this.state.feeAmount),
- address: address,
- };
-
- doPublish();
- });
- } else {
- doPublish();
- }
- }
-
- handlePublishStarted() {
- this.setState({
- modal: "publishStarted",
- });
- }
-
- handlePublishStartedConfirmed() {
- this.props.navigate("/published");
- }
-
- handlePublishError(error) {
- this.setState({
- submitting: false,
- modal: "error",
- errorMessage: error.message,
- });
- }
-
- claim() {
- const { claimsByUri } = this.props;
- const { uri } = this.state;
-
- return claimsByUri[uri];
- }
-
- topClaimValue() {
- if (!this.claim()) return null;
-
- return parseFloat(this.claim().amount);
- }
-
- myClaimExists() {
- const { myClaims } = this.props;
- const { name } = this.state;
-
- if (!name) return false;
-
- return !!myClaims.find(claim => claim.name === name);
- }
-
- topClaimIsMine() {
- const myClaimInfo = this.myClaimInfo();
- const { claimsByUri } = this.props;
- const { uri } = this.state;
-
- if (!uri) return null;
-
- const claim = claimsByUri[uri];
-
- if (!claim) return true;
- if (!myClaimInfo) return false;
-
- return myClaimInfo.amount >= claimInfo.amount;
- }
-
- myClaimInfo() {
- const { name } = this.state;
-
- return Object.values(this.props.myClaims).find(
- claim => claim.name === name
- );
- }
-
- handleNameChange(event) {
- var rawName = event.target.value;
-
- this.nameChanged(rawName);
- }
-
- nameChanged(rawName) {
- if (!rawName) {
- this.setState({
- rawName: "",
- name: "",
- uri: "",
- });
-
- return;
- }
-
- if (!lbryuri.isValidName(rawName, false)) {
- this.refs.name.showError(
- __("LBRY names must contain only letters, numbers and dashes.")
- );
- return;
- }
-
- let channel = "";
- if (this.state.channel !== "anonymous") channel = this.state.channel;
-
- const name = rawName.toLowerCase();
- const uri = lbryuri.build({ contentName: name, channelName: channel });
- this.setState({
- rawName: rawName,
- name: name,
- prefillDone: false,
- uri,
- });
-
- if (this.resolveUriTimeout) {
- clearTimeout(this.resolveUriTimeout);
- this.resolveUriTimeout = undefined;
- }
- const resolve = () => this.props.resolveUri(uri);
-
- this.resolveUriTimeout = setTimeout(resolve.bind(this), 500, {
- once: true,
- });
- }
-
- handlePrefillClicked() {
- const {license, licenseUrl, title, thumbnail, description,
- language, nsfw} = this.myClaimInfo().value.stream.metadata;
-
- let newState = {
- meta_title: title,
- meta_thumbnail: thumbnail,
- meta_description: description,
- meta_language: language,
- meta_nsfw: nsfw,
- };
-
- if (license == this._defaultCopyrightNotice) {
- newState.licenseType = "copyright";
- newState.copyrightNotice = this._defaultCopyrightNotice;
- } else {
- // If the license URL or description matches one of the drop-down options, use that
- let licenseType = "other"; // Will be overridden if we find a match
- for (let option of this._meta_license.getOptions()) {
- if (
- option.getAttribute("data-url") === licenseUrl ||
- option.text === license
- ) {
- licenseType = option.value;
- }
- }
-
- if (licenseType == "other") {
- newState.otherLicenseDescription = license;
- newState.otherLicenseUrl = licenseUrl;
- }
- newState.licenseType = licenseType;
- }
-
- this.setState(newState);
- }
-
- handleBidChange(event) {
- this.setState({
- bid: event.target.value,
- });
- }
-
- handleFeeAmountChange(event) {
- this.setState({
- feeAmount: event.target.value,
- });
- }
-
- handleFeeCurrencyChange(event) {
- this.setState({
- feeCurrency: event.target.value,
- });
- }
-
- handleFeePrefChange(feeEnabled) {
- this.setState({
- isFee: feeEnabled,
- });
- }
-
- handleMetadataChange(event) {
- /**
- * This function is used for all metadata inputs that store the final value directly into state.
- * The only exceptions are inputs related to license description and license URL, which require
- * more complex logic and the final value is determined at submit time.
- */
- this.setState({
- ["meta_" + event.target.name]: event.target.value,
- });
- }
-
- handleLicenseTypeChange(event) {
- this.setState({
- licenseType: event.target.value,
- });
- }
-
- handleCopyrightNoticeChange(event) {
- this.setState({
- copyrightNotice: event.target.value,
- });
- }
-
- handleOtherLicenseDescriptionChange(event) {
- this.setState({
- otherLicenseDescription: event.target.value,
- });
- }
-
- handleOtherLicenseUrlChange(event) {
- this.setState({
- otherLicenseUrl: event.target.value,
- });
- }
-
- handleChannelChange(channelName) {
- this.setState({
- channel: channelName,
- });
- const nameChanged = () => this.nameChanged(this.state.rawName);
- setTimeout(nameChanged.bind(this), 500, { once: true });
- }
-
- handleTOSChange(event) {
- this.setState({
- tosAgree: event.target.checked,
- });
- }
-
- handleCreateChannelClick(event) {
- if (this.state.newChannelName.length < 5) {
- this.refs.newChannelName.showError(
- __("LBRY channel names must be at least 4 characters in length.")
- );
- return;
- }
-
- this.setState({
- creatingChannel: true,
- });
-
- const newChannelName = this.state.newChannelName;
- lbry
- .channel_new({
- channel_name: newChannelName,
- amount: parseFloat(this.state.newChannelBid),
- })
- .then(
- () => {
- setTimeout(() => {
- this.setState({
- creatingChannel: false,
- });
-
- this._updateChannelList(newChannelName);
- }, 10000);
- },
- error => {
- // TODO: better error handling
- this.refs.newChannelName.showError(
- __("Unable to create channel due to an internal error.")
- );
- this.setState({
- creatingChannel: false,
- });
- }
- );
- }
-
- getLicense() {
- switch (this.state.licenseType) {
- case "copyright":
- return this.state.copyrightNotice;
- case "other":
- return this.state.otherLicenseDescription;
- default:
- return this._meta_license.getSelectedElement().text;
- }
- }
-
- getLicenseUrl() {
- switch (this.state.licenseType) {
- case "copyright":
- return "";
- case "other":
- return this.state.otherLicenseUrl;
- default:
- return this._meta_license.getSelectedElement().getAttribute("data-url");
- }
- }
-
- componentWillMount() {
- this.props.fetchClaimListMine();
- this._updateChannelList();
- }
-
- onFileChange() {
- if (this.refs.file.getValue()) {
- this.setState({ hasFile: true });
- } else {
- this.setState({ hasFile: false });
- }
- }
-
- getNameBidHelpText() {
- if (
- this.state.uri &&
- this.props.resolvingUris.indexOf(this.state.uri) !== -1 &&
- this.claim() === undefined
- ) {
- return __("Checking...");
- } else if (!this.state.name) {
- return __("Select a URL for this publish.");
- } else if (!this.claim()) {
- return __("This URL is unused.");
- } else if (this.myClaimExists() && !this.state.prefillDone) {
- return (
-
- {__("You already have a claim with this name.")}{" "}
- this.handlePrefillClicked()}
- />
-
- );
- } else if (this.claim()) {
- if (this.topClaimValue() === 1) {
- return (
-
- {__(
- 'A deposit of at least one credit is required to win "%s". However, you can still get a permanent URL for any amount.',
- this.state.name
- )}
-
- );
- } else {
- return (
-
- {__(
- 'A deposit of at least "%s" credits is required to win "%s". However, you can still get a permanent URL for any amount.',
- this.topClaimValue(),
- this.state.name
- )}
-
- );
- }
- } else {
- return "";
- }
- }
-
- closeModal() {
- this.setState({
- modal: null,
- });
- }
-
- render() {
- const lbcInputHelp = __(
- "This LBC remains yours and the deposit can be undone at any time."
- );
-
- return (
-
-
-
- {
- this.handlePublishStartedConfirmed(event);
- }}
- >
-
- {__("Your file has been published to LBRY at the address")}
- {" "}{this.state.uri}
!
-
-
- {__(
- 'The file will take a few minutes to appear for other LBRY users. Until then it will be listed as "pending" under your published files.'
- )}
-
-
- {
- this.closeModal(event);
- }}
- >
- {__(
- "The following error occurred when attempting to publish your file"
- )}: {this.state.errorMessage}
-
-
- );
- }
-}
-
-class ChannelSection extends React.PureComponent {
- constructor(props) {
- super(props);
-
- this.state = {
- newChannelName: "@",
- newChannelBid: 10,
- addingChannel: false,
- };
- }
-
- handleChannelChange(event) {
- const channel = event.target.value;
- if (channel === "new") this.setState({ addingChannel: true });
- else {
- this.setState({ addingChannel: false });
- this.props.handleChannelChange(event.target.value);
- }
- }
-
- handleNewChannelNameChange(event) {
- const newChannelName = event.target.value.startsWith("@")
- ? event.target.value
- : "@" + event.target.value;
-
- if (
- newChannelName.length > 1 &&
- !lbryuri.isValidName(newChannelName.substr(1), false)
- ) {
- this.refs.newChannelName.showError(
- __("LBRY channel names must contain only letters, numbers and dashes.")
- );
- return;
- } else {
- this.refs.newChannelName.clearError();
- }
-
- this.setState({
- newChannelName,
- });
- }
-
- handleNewChannelBidChange(event) {
- this.setState({
- newChannelBid: event.target.value,
- });
- }
-
- handleCreateChannelClick(event) {
- if (this.state.newChannelName.length < 5) {
- this.refs.newChannelName.showError(
- __("LBRY channel names must be at least 4 characters in length.")
- );
- return;
- }
-
- this.setState({
- creatingChannel: true,
- });
-
- const newChannelName = this.state.newChannelName;
- const amount = parseFloat(this.state.newChannelBid);
- this.setState({
- creatingChannel: true,
- });
- const success = (() => {
- this.setState({
- creatingChannel: false,
- addingChannel: false,
- channel: newChannelName,
- });
- this.props.handleChannelChange(newChannelName);
- }).bind(this);
- const failure = (err => {
- this.setState({
- creatingChannel: false,
- });
- this.refs.newChannelName.showError(
- __("Unable to create channel due to an internal error.")
- );
- }).bind(this);
- this.props.createChannel(newChannelName, amount).then(success, failure);
- }
-
- render() {
- const lbcInputHelp = __(
- "This LBC remains yours and the deposit can be undone at any time."
- );
-
- const { fetchingChannels, channels } = this.props;
-
- let channelContent = [];
- if (channels.length > 0) {
- channelContent.push(
-
-
- {__("Anonymous")}
-
- {this.props.channels.map(({ name }) =>
- {name}
- )}
-
- {__("New identity...")}
-
-
- );
- if (fetchingChannels) {
- channelContent.push(
-
- );
- }
- } else if (fetchingChannels) {
- channelContent.push(
-
- );
- }
-
- return (
-
-
-
{__("Identity")}
-
- {__("Who created this content?")}
-
-
-
- {channelContent}
-
- {this.state.addingChannel &&
-
-
{
- this.handleNewChannelNameChange(event);
- }}
- value={this.state.newChannelName}
- />
-
-
-
-
- }
-
- );
- }
-}
+const PublishPage = props => {
+ return
;
+};
export default PublishPage;
From 7c3953ac517803c2ac348f420aca42dc99abe662 Mon Sep 17 00:00:00 2001
From: 6ea86b96 <6ea86b96@gmail.com>
Date: Mon, 3 Jul 2017 14:53:56 +0700
Subject: [PATCH 07/26] Fix selectClaimForUriIsMine
---
ui/js/selectors/claims.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ui/js/selectors/claims.js b/ui/js/selectors/claims.js
index 3478c9fc4..aa19a6e80 100644
--- a/ui/js/selectors/claims.js
+++ b/ui/js/selectors/claims.js
@@ -51,7 +51,7 @@ export const makeSelectClaimForUri = () => {
const selectClaimForUriIsMine = (state, props) => {
const uri = lbryuri.normalize(props.uri);
const claim = selectClaimsByUri(state)[uri];
- const myClaims = selectMyClaims(state);
+ const myClaims = selectMyClaimsRaw(state);
return myClaims.has(claim.claim_id);
};
From 35a5cb0918e813f67857fe5626621996cef04bab Mon Sep 17 00:00:00 2001
From: 6ea86b96 <6ea86b96@gmail.com>
Date: Tue, 4 Jul 2017 13:48:52 +0700
Subject: [PATCH 08/26] Fix file info selector
---
ui/js/selectors/file_info.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ui/js/selectors/file_info.js b/ui/js/selectors/file_info.js
index 5b0e4941b..4010b95c8 100644
--- a/ui/js/selectors/file_info.js
+++ b/ui/js/selectors/file_info.js
@@ -102,7 +102,7 @@ export const selectFileInfosPublished = createSelector(
const fileInfo = byOutpoint[outpoint];
if (fileInfo) fileInfos.push(fileInfo);
});
- return fileInfos;
+ return [...fileInfos, ...pendingPublish];
}
);
From 6c6f1beb19b02faa03180c12f4d57295e7080b3e Mon Sep 17 00:00:00 2001
From: 6ea86b96 <6ea86b96@gmail.com>
Date: Sat, 1 Jul 2017 18:03:51 +0700
Subject: [PATCH 09/26] Change redux-persist debounce to 10 seconds
---
ui/js/store.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ui/js/store.js b/ui/js/store.js
index 8e6c11949..35f7ab2ec 100644
--- a/ui/js/store.js
+++ b/ui/js/store.js
@@ -102,7 +102,7 @@ const persistOptions = {
// Order is important. Needs to be compressed last or other transforms can't
// read the data
transforms: [saveClaimsFilter, saveFileInfosFilter, compressor],
- debounce: 1000,
+ debounce: 10000,
storage: localForage,
};
window.cacheStore = persistStore(reduxStore, persistOptions);
From f3fdf5e84177cc894904c1146075dcdd24787cac Mon Sep 17 00:00:00 2001
From: 6ea86b96 <6ea86b96@gmail.com>
Date: Tue, 4 Jul 2017 18:05:35 +0700
Subject: [PATCH 10/26] Rename pending to fetching in selectors to avoid
confusion
---
ui/js/actions/file_info.js | 16 ++++++++--------
ui/js/page/fileListDownloaded/index.js | 4 ++--
ui/js/page/fileListDownloaded/view.jsx | 8 ++++----
ui/js/page/fileListPublished/index.js | 4 ++--
ui/js/page/fileListPublished/view.jsx | 8 ++++----
ui/js/reducers/claims.js | 4 ++--
ui/js/reducers/file_info.js | 26 ++------------------------
ui/js/selectors/claims.js | 4 ++--
ui/js/selectors/file_info.js | 16 ++++++++--------
9 files changed, 34 insertions(+), 56 deletions(-)
diff --git a/ui/js/actions/file_info.js b/ui/js/actions/file_info.js
index 9d3861fb7..70db7244d 100644
--- a/ui/js/actions/file_info.js
+++ b/ui/js/actions/file_info.js
@@ -3,11 +3,11 @@ import lbry from "lbry";
import { doFetchClaimListMine } from "actions/content";
import {
selectClaimsByUri,
- selectClaimListMineIsPending,
+ selectIsFetchingClaimListMine,
selectMyClaimsOutpoints,
} from "selectors/claims";
import {
- selectFileListIsPending,
+ selectIsFetchingFileList,
selectFileInfosByOutpoint,
selectUrisLoading,
} from "selectors/file_info";
@@ -48,9 +48,9 @@ export function doFetchFileInfo(uri) {
export function doFileList() {
return function(dispatch, getState) {
const state = getState();
- const isPending = selectFileListIsPending(state);
+ const isFetching = selectIsFetchingFileList(state);
- if (!isPending) {
+ if (!isFetching) {
dispatch({
type: types.FILE_LIST_STARTED,
});
@@ -128,10 +128,10 @@ export function doDeleteFile(outpoint, deleteFromComputer, abandonClaim) {
export function doFetchFileInfosAndPublishedClaims() {
return function(dispatch, getState) {
const state = getState(),
- isClaimListMinePending = selectClaimListMineIsPending(state),
- isFileInfoListPending = selectFileListIsPending(state);
+ isFetchingClaimListMine = selectIsFetchingClaimListMine(state),
+ isFetchingFileInfo = selectIsFetchingFileList(state);
- dispatch(doFetchClaimListMine());
- dispatch(doFileList());
+ if (!isFetchingClaimListMine) dispatch(doFetchClaimListMine());
+ if (!isFetchingFileInfo) dispatch(doFileList());
};
}
diff --git a/ui/js/page/fileListDownloaded/index.js b/ui/js/page/fileListDownloaded/index.js
index 86d26d851..e51d5389f 100644
--- a/ui/js/page/fileListDownloaded/index.js
+++ b/ui/js/page/fileListDownloaded/index.js
@@ -3,7 +3,7 @@ import { connect } from "react-redux";
import { doFetchFileInfosAndPublishedClaims } from "actions/file_info";
import {
selectFileInfosDownloaded,
- selectFileListDownloadedOrPublishedIsPending,
+ selectIsFetchingFileListDownloadedOrPublished,
} from "selectors/file_info";
import { doNavigate } from "actions/app";
import { doCancelAllResolvingUris } from "actions/content";
@@ -11,7 +11,7 @@ import FileListDownloaded from "./view";
const select = state => ({
fileInfos: selectFileInfosDownloaded(state),
- isPending: selectFileListDownloadedOrPublishedIsPending(state),
+ isFetching: selectIsFetchingFileListDownloadedOrPublished(state),
});
const perform = dispatch => ({
diff --git a/ui/js/page/fileListDownloaded/view.jsx b/ui/js/page/fileListDownloaded/view.jsx
index 03665847c..c1501ec78 100644
--- a/ui/js/page/fileListDownloaded/view.jsx
+++ b/ui/js/page/fileListDownloaded/view.jsx
@@ -12,7 +12,7 @@ import SubHeader from "component/subHeader";
class FileListDownloaded extends React.PureComponent {
componentWillMount() {
- if (!this.props.isPending) this.props.fetchFileInfosDownloaded();
+ if (!this.props.isFetching) this.props.fetchFileInfosDownloaded();
}
componentWillUnmount() {
@@ -20,13 +20,13 @@ class FileListDownloaded extends React.PureComponent {
}
render() {
- const { fileInfos, isPending, navigate } = this.props;
+ const { fileInfos, isFetching, navigate } = this.props;
let content;
if (fileInfos && fileInfos.length > 0) {
- content =
;
+ content =
;
} else {
- if (isPending) {
+ if (isFetching) {
content =
;
} else {
content = (
diff --git a/ui/js/page/fileListPublished/index.js b/ui/js/page/fileListPublished/index.js
index 7e5e349c3..9b8b2b80b 100644
--- a/ui/js/page/fileListPublished/index.js
+++ b/ui/js/page/fileListPublished/index.js
@@ -4,7 +4,7 @@ import { connect } from "react-redux";
import { doFetchFileInfosAndPublishedClaims } from "actions/file_info";
import {
selectFileInfosPublished,
- selectFileListDownloadedOrPublishedIsPending,
+ selectIsFetchingFileListDownloadedOrPublished,
} from "selectors/file_info";
import { doClaimRewardType } from "actions/rewards";
import { doNavigate } from "actions/app";
@@ -13,7 +13,7 @@ import FileListPublished from "./view";
const select = state => ({
fileInfos: selectFileInfosPublished(state),
- isPending: selectFileListDownloadedOrPublishedIsPending(state),
+ isFetching: selectIsFetchingFileListDownloadedOrPublished(state),
});
const perform = dispatch => ({
diff --git a/ui/js/page/fileListPublished/view.jsx b/ui/js/page/fileListPublished/view.jsx
index 822cfeb7d..fdcbe97cd 100644
--- a/ui/js/page/fileListPublished/view.jsx
+++ b/ui/js/page/fileListPublished/view.jsx
@@ -12,7 +12,7 @@ import SubHeader from "component/subHeader";
class FileListPublished extends React.PureComponent {
componentWillMount() {
- if (!this.props.isPending) this.props.fetchFileListPublished();
+ if (!this.props.isFetching) this.props.fetchFileListPublished();
}
componentDidUpdate() {
@@ -24,7 +24,7 @@ class FileListPublished extends React.PureComponent {
}
render() {
- const { fileInfos, isPending, navigate } = this.props;
+ const { fileInfos, isFetching, navigate } = this.props;
let content;
@@ -32,12 +32,12 @@ class FileListPublished extends React.PureComponent {
content = (
);
} else {
- if (isPending) {
+ if (isFetching) {
content =
;
} else {
content = (
diff --git a/ui/js/reducers/claims.js b/ui/js/reducers/claims.js
index 75bfc4a52..3ee992f94 100644
--- a/ui/js/reducers/claims.js
+++ b/ui/js/reducers/claims.js
@@ -34,7 +34,7 @@ reducers[types.RESOLVE_URI_COMPLETED] = function(state, action) {
reducers[types.FETCH_CLAIM_LIST_MINE_STARTED] = function(state, action) {
return Object.assign({}, state, {
- isClaimListMinePending: true,
+ isFetchingClaimListMine: true,
});
};
@@ -50,7 +50,7 @@ reducers[types.FETCH_CLAIM_LIST_MINE_COMPLETED] = function(state, action) {
});
return Object.assign({}, state, {
- isClaimListMinePending: false,
+ isFetchingClaimListMine: false,
myClaims: myClaims,
byId,
});
diff --git a/ui/js/reducers/file_info.js b/ui/js/reducers/file_info.js
index fe6979045..e462f3cf6 100644
--- a/ui/js/reducers/file_info.js
+++ b/ui/js/reducers/file_info.js
@@ -6,7 +6,7 @@ const defaultState = {};
reducers[types.FILE_LIST_STARTED] = function(state, action) {
return Object.assign({}, state, {
- isFileListPending: true,
+ isFetchingFileList: true,
});
};
@@ -22,7 +22,7 @@ reducers[types.FILE_LIST_COMPLETED] = function(state, action) {
});
return Object.assign({}, state, {
- isFileListPending: false,
+ isFetchingFileList: false,
byOutpoint: newByOutpoint,
pendingByOutpoint,
});
@@ -171,28 +171,6 @@ reducers[types.PUBLISH_FAILED] = function(state, action) {
});
};
-// reducers[types.PUBLISH_COMPLETED] = function(state, action) {
-// const { claim } = action.data;
-// const uri = lbryuri.build({
-// txid: claim.txId
-// })
-// const newPendingPublish = {
-// name,
-// channel_name,
-// claim_id: "pending_claim_" + uri,
-// txid: "pending_" + uri,
-// nout: 0,
-// outpoint: "pending_" + uri + ":0",
-// time: Date.now(),
-// };
-// const fileInfos = Object.assign({}, state.fileInfos)
-// fileInfos[newPendingPublish.outpoint] = newPendingPublish
-
-// return Object.assign({}, state, {
-// fileInfos,
-// })
-// }
-
export default function reducer(state = defaultState, action) {
const handler = reducers[action.type];
if (handler) return handler(state, action);
diff --git a/ui/js/selectors/claims.js b/ui/js/selectors/claims.js
index aa19a6e80..f0708cf86 100644
--- a/ui/js/selectors/claims.js
+++ b/ui/js/selectors/claims.js
@@ -100,9 +100,9 @@ export const makeSelectContentTypeForUri = () => {
);
};
-export const selectClaimListMineIsPending = createSelector(
+export const selectIsFetchingClaimListMine = createSelector(
_selectState,
- state => state.isClaimListMinePending
+ state => !!state.isFetchingClaimListMine
);
export const selectMyClaimsRaw = createSelector(
diff --git a/ui/js/selectors/file_info.js b/ui/js/selectors/file_info.js
index 4010b95c8..7b8a10769 100644
--- a/ui/js/selectors/file_info.js
+++ b/ui/js/selectors/file_info.js
@@ -2,7 +2,7 @@ import lbry from "lbry";
import { createSelector } from "reselect";
import {
selectClaimsByUri,
- selectClaimListMineIsPending,
+ selectIsFetchingClaimListMine,
selectMyClaimsOutpoints,
} from "selectors/claims";
@@ -13,16 +13,16 @@ export const selectFileInfosByOutpoint = createSelector(
state => state.byOutpoint || {}
);
-export const selectFileListIsPending = createSelector(
+export const selectIsFetchingFileList = createSelector(
_selectState,
- state => state.isFileListPending
+ state => !!state.isFetchingFileList
);
-export const selectFileListDownloadedOrPublishedIsPending = createSelector(
- selectFileListIsPending,
- selectClaimListMineIsPending,
- (isFileListPending, isClaimListMinePending) =>
- isFileListPending || isClaimListMinePending
+export const selectIsFetchingFileListDownloadedOrPublished = createSelector(
+ selectIsFetchingFileList,
+ selectIsFetchingClaimListMine,
+ (isFetchingFileList, isFetchingClaimListMine) =>
+ isFetchingFileList || isFetchingClaimListMine
);
export const selectFileInfoForUri = (state, props) => {
From 253932113f11e55694490f11c1f5b07299946328 Mon Sep 17 00:00:00 2001
From: 6ea86b96 <6ea86b96@gmail.com>
Date: Tue, 4 Jul 2017 19:51:05 +0700
Subject: [PATCH 11/26] Fix adding new identities
---
.../publishForm/internal/channelSection.jsx | 51 +++++++++----------
1 file changed, 23 insertions(+), 28 deletions(-)
diff --git a/ui/js/component/publishForm/internal/channelSection.jsx b/ui/js/component/publishForm/internal/channelSection.jsx
index c0c4bf473..274f30bdf 100644
--- a/ui/js/component/publishForm/internal/channelSection.jsx
+++ b/ui/js/component/publishForm/internal/channelSection.jsx
@@ -2,6 +2,7 @@ import React from "react";
import lbryuri from "lbryuri";
import { FormField, FormRow } from "component/form.js";
import { BusyMessage } from "component/common";
+import Link from "component/link";
class ChannelSection extends React.PureComponent {
constructor(props) {
@@ -92,37 +93,31 @@ class ChannelSection extends React.PureComponent {
"This LBC remains yours and the deposit can be undone at any time."
);
- const { fetchingChannels, channels } = this.props;
+ const { fetchingChannels, channels = [] } = this.props;
let channelContent = [];
- if (channels.length > 0) {
+ channelContent.push(
+
+
+ {__("Anonymous")}
+
+ {this.props.channels.map(({ name }) =>
+ {name}
+ )}
+
+ {__("New identity...")}
+
+
+ );
+ if (fetchingChannels) {
channelContent.push(
-
-
- {__("Anonymous")}
-
- {this.props.channels.map(({ name }) =>
- {name}
- )}
-
- {__("New identity...")}
-
-
- );
- if (fetchingChannels) {
- channelContent.push(
-
- );
- }
- } else if (fetchingChannels) {
- channelContent.push(
-
+
);
}
From 5012d44384e8da43cbba400f1d06caf468b72b93 Mon Sep 17 00:00:00 2001
From: 6ea86b96 <6ea86b96@gmail.com>
Date: Wed, 5 Jul 2017 13:38:17 +0700
Subject: [PATCH 12/26] Start using claims instead of file info for published
files
---
ui/js/component/fileList/view.jsx | 4 +++-
ui/js/page/fileListPublished/index.js | 14 +++++++-------
ui/js/page/fileListPublished/view.jsx | 10 +++++-----
3 files changed, 15 insertions(+), 13 deletions(-)
diff --git a/ui/js/component/fileList/view.jsx b/ui/js/component/fileList/view.jsx
index 20631b59e..8785972d9 100644
--- a/ui/js/component/fileList/view.jsx
+++ b/ui/js/component/fileList/view.jsx
@@ -67,7 +67,9 @@ class FileList extends React.PureComponent {
const content = [];
this._sortFunctions[sortBy](fileInfos).forEach(fileInfo => {
- let uriParams = {};
+ let uriParams = {
+ claimId: fileInfo.claim_id,
+ };
if (fileInfo.channel_name) {
uriParams.channelName = fileInfo.channel_name;
uriParams.contentName = fileInfo.name;
diff --git a/ui/js/page/fileListPublished/index.js b/ui/js/page/fileListPublished/index.js
index 9b8b2b80b..bd00db2ed 100644
--- a/ui/js/page/fileListPublished/index.js
+++ b/ui/js/page/fileListPublished/index.js
@@ -1,24 +1,24 @@
import React from "react";
import rewards from "rewards";
import { connect } from "react-redux";
-import { doFetchFileInfosAndPublishedClaims } from "actions/file_info";
+import { doFetchClaimListMine } from "actions/content";
import {
- selectFileInfosPublished,
- selectIsFetchingFileListDownloadedOrPublished,
-} from "selectors/file_info";
+ selectMyClaims,
+ selectIsFetchingClaimListMine,
+} from "selectors/claims";
import { doClaimRewardType } from "actions/rewards";
import { doNavigate } from "actions/app";
import { doCancelAllResolvingUris } from "actions/content";
import FileListPublished from "./view";
const select = state => ({
- fileInfos: selectFileInfosPublished(state),
- isFetching: selectIsFetchingFileListDownloadedOrPublished(state),
+ claims: selectMyClaims(state),
+ isFetching: selectIsFetchingClaimListMine(state),
});
const perform = dispatch => ({
navigate: path => dispatch(doNavigate(path)),
- fetchFileListPublished: () => dispatch(doFetchFileInfosAndPublishedClaims()),
+ fetchClaims: () => dispatch(doFetchClaimListMine()),
claimFirstPublishReward: () =>
dispatch(doClaimRewardType(rewards.TYPE_FIRST_PUBLISH)),
cancelResolvingUris: () => dispatch(doCancelAllResolvingUris()),
diff --git a/ui/js/page/fileListPublished/view.jsx b/ui/js/page/fileListPublished/view.jsx
index fdcbe97cd..a7b500b29 100644
--- a/ui/js/page/fileListPublished/view.jsx
+++ b/ui/js/page/fileListPublished/view.jsx
@@ -12,11 +12,11 @@ import SubHeader from "component/subHeader";
class FileListPublished extends React.PureComponent {
componentWillMount() {
- if (!this.props.isFetching) this.props.fetchFileListPublished();
+ if (!this.props.isFetching) this.props.fetchClaims();
}
componentDidUpdate() {
- if (this.props.fileInfos.length > 0) this.props.claimFirstPublishReward();
+ // if (this.props.claims.length > 0) this.props.fetchClaims();
}
componentWillUnmount() {
@@ -24,14 +24,14 @@ class FileListPublished extends React.PureComponent {
}
render() {
- const { fileInfos, isFetching, navigate } = this.props;
+ const { claims, isFetching, navigate } = this.props;
let content;
- if (fileInfos && fileInfos.length > 0) {
+ if (claims && claims.length > 0) {
content = (
From f9b9221471239fb541663cfb315accdb75e0eee5 Mon Sep 17 00:00:00 2001
From: 6ea86b96 <6ea86b96@gmail.com>
Date: Thu, 6 Jul 2017 14:47:02 +0700
Subject: [PATCH 13/26] Fix show page being blank for unconfirmed claims
---
ui/js/page/showPage/view.jsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ui/js/page/showPage/view.jsx b/ui/js/page/showPage/view.jsx
index 05b8b0b91..687cb5498 100644
--- a/ui/js/page/showPage/view.jsx
+++ b/ui/js/page/showPage/view.jsx
@@ -24,7 +24,7 @@ class ShowPage extends React.PureComponent {
let innerContent = "";
- if (isResolvingUri && !claim) {
+ if ((isResolvingUri && !claim) || !claim) {
innerContent = (
From 470f61da9d0eace14caac61d56bec590b8d7ac15 Mon Sep 17 00:00:00 2001
From: 6ea86b96 <6ea86b96@gmail.com>
Date: Sat, 8 Jul 2017 15:03:12 +0700
Subject: [PATCH 14/26] commit little and often fail
---
ui/js/actions/content.js | 6 ++-
ui/js/actions/file_info.js | 22 ++++++----
ui/js/component/fileList/view.jsx | 1 -
ui/js/component/fileTile/view.jsx | 11 ++++-
ui/js/component/publishForm/view.jsx | 6 ++-
ui/js/page/fileListPublished/index.js | 4 +-
ui/js/reducers/claims.js | 62 +++++++++++++++++++++++++--
ui/js/reducers/file_info.js | 33 --------------
ui/js/selectors/claims.js | 21 +++++++--
ui/js/store.js | 9 ++--
10 files changed, 114 insertions(+), 61 deletions(-)
diff --git a/ui/js/actions/content.js b/ui/js/actions/content.js
index ca6f1850d..291dc4d81 100644
--- a/ui/js/actions/content.js
+++ b/ui/js/actions/content.js
@@ -396,14 +396,16 @@ export function doPublish(params) {
} else {
uri = lbryuri.build({ name: name }, false);
}
+ const fakeId = "pending";
const pendingPublish = {
name,
channel_name,
- claim_id: "pending_claim_" + uri,
+ claim_id: fakeId,
txid: "pending_" + uri,
nout: 0,
- outpoint: "pending_" + uri + ":0",
+ outpoint: fakeId + ":0",
time: Date.now(),
+ pending: true,
};
dispatch({
diff --git a/ui/js/actions/file_info.js b/ui/js/actions/file_info.js
index 70db7244d..eb4a6753f 100644
--- a/ui/js/actions/file_info.js
+++ b/ui/js/actions/file_info.js
@@ -102,14 +102,20 @@ export function doDeleteFile(outpoint, deleteFromComputer, abandonClaim) {
},
});
- const success = () => {
- dispatch({
- type: types.ABANDON_CLAIM_COMPLETED,
- data: {
- claimId: fileInfo.claim_id,
- },
- });
- };
+ // We need to run this after a few seconds or the claim gets added back
+ // to the store again by an already running fetch claims query.
+ const success = setTimeout(
+ () => {
+ dispatch({
+ type: types.ABANDON_CLAIM_COMPLETED,
+ data: {
+ claimId: fileInfo.claim_id,
+ },
+ });
+ },
+ 10000,
+ { once: true }
+ );
lbry.claim_abandon({ claim_id: fileInfo.claim_id }).then(success);
}
}
diff --git a/ui/js/component/fileList/view.jsx b/ui/js/component/fileList/view.jsx
index 8785972d9..edd64b993 100644
--- a/ui/js/component/fileList/view.jsx
+++ b/ui/js/component/fileList/view.jsx
@@ -96,7 +96,6 @@ class FileList extends React.PureComponent {
{__("Date")}
{__("Title")}
- {__("File name")}
{content}
diff --git a/ui/js/component/fileTile/view.jsx b/ui/js/component/fileTile/view.jsx
index c0c25a202..eb865d1ac 100644
--- a/ui/js/component/fileTile/view.jsx
+++ b/ui/js/component/fileTile/view.jsx
@@ -64,9 +64,16 @@ class FileTile extends React.PureComponent {
const isClaimable = lbryuri.isClaimable(uri);
const title = isClaimed && metadata && metadata.title
? metadata.title
- : uri;
+ : lbryuri.parse(uri).contentName;
const obscureNsfw = this.props.obscureNsfw && metadata && metadata.nsfw;
- let onClick = () => navigate("/show", { uri });
+ let onClick;
+ if (isClaimed) {
+ onClick = () => navigate("/show", { uri });
+ } else {
+ onClick = () => {
+ return false;
+ };
+ }
let description = "";
if (isClaimed) {
diff --git a/ui/js/component/publishForm/view.jsx b/ui/js/component/publishForm/view.jsx
index 91fd5eb09..446081f6d 100644
--- a/ui/js/component/publishForm/view.jsx
+++ b/ui/js/component/publishForm/view.jsx
@@ -873,7 +873,11 @@ class PublishForm extends React.PureComponent {
onClick={event => {
this.handleSubmit(event);
}}
- disabled={this.state.submitting}
+ disabled={
+ this.state.submitting ||
+ (this.state.uri &&
+ this.props.resolvingUris.indexOf(this.state.uri) !== -1)
+ }
/>
({
- claims: selectMyClaims(state),
+ claims: selectMyClaimsWithoutChannels(state),
isFetching: selectIsFetchingClaimListMine(state),
});
diff --git a/ui/js/reducers/claims.js b/ui/js/reducers/claims.js
index 3ee992f94..8f60bf9a1 100644
--- a/ui/js/reducers/claims.js
+++ b/ui/js/reducers/claims.js
@@ -40,19 +40,39 @@ reducers[types.FETCH_CLAIM_LIST_MINE_STARTED] = function(state, action) {
reducers[types.FETCH_CLAIM_LIST_MINE_COMPLETED] = function(state, action) {
const { claims } = action.data;
- const myClaims = new Set(state.myClaims);
const byUri = Object.assign({}, state.claimsByUri);
const byId = Object.assign({}, state.byId);
+ const pendingById = Object.assign({}, state.pendingById);
+
+ const myClaims = new Set(claims.map(claim => claim.claim_id));
claims.forEach(claim => {
- myClaims.add(claim.claim_id);
byId[claim.claim_id] = claim;
+
+ const pending = Object.values(pendingById).find(pendingClaim => {
+ return (
+ pendingClaim.name == claim.name &&
+ pendingClaim.channel_name == claim.channel_name
+ );
+ });
+
+ if (pending) {
+ delete pendingById[pending.claim_id];
+ }
});
+ // Remove old timed out pending publishes
+ const old = Object.values(pendingById)
+ .filter(pendingClaim => Date.now() - pendingClaim.time >= 20 * 60 * 1000)
+ .forEach(pendingClaim => {
+ delete pendingById[pendingClaim.claim_id];
+ });
+
return Object.assign({}, state, {
isFetchingClaimListMine: false,
myClaims: myClaims,
byId,
+ pendingById,
});
};
@@ -91,6 +111,17 @@ reducers[types.FETCH_CHANNEL_CLAIMS_COMPLETED] = function(state, action) {
});
};
+reducers[types.ABANDON_CLAIM_STARTED] = function(state, action) {
+ const { claimId } = action.data;
+ const abandoningById = Object.assign({}, state.abandoningById);
+
+ abandoningById[claimId] = true;
+
+ return Object.assign({}, state, {
+ abandoningById,
+ });
+};
+
reducers[types.ABANDON_CLAIM_COMPLETED] = function(state, action) {
const { claimId } = action.data;
const myClaims = new Set(state.myClaims);
@@ -128,17 +159,42 @@ reducers[types.CREATE_CHANNEL_COMPLETED] = function(state, action) {
});
};
+reducers[types.PUBLISH_STARTED] = function(state, action) {
+ const { pendingPublish } = action.data;
+ const pendingById = Object.assign({}, state.pendingById);
+
+ pendingById[pendingPublish.claim_id] = pendingPublish;
+
+ return Object.assign({}, state, {
+ pendingById,
+ });
+};
+
reducers[types.PUBLISH_COMPLETED] = function(state, action) {
- const { claim } = action.data;
+ const { claim, pendingPublish } = action.data;
const byId = Object.assign({}, state.byId);
const myClaims = new Set(state.myClaims);
+ const pendingById = Object.assign({}, state.pendingById);
byId[claim.claim_id] = claim;
myClaims.add(claim.claim_id);
+ delete pendingById[pendingPublish.claim_id];
return Object.assign({}, state, {
byId,
myClaims,
+ pendingById,
+ });
+};
+
+reducers[types.PUBLISH_FAILED] = function(state, action) {
+ const { pendingPublish } = action.data;
+ const pendingById = Object.assign({}, state.pendingById);
+
+ delete pendingById[pendingPublish.claim_id];
+
+ return Object.assign({}, state, {
+ pendingById,
});
};
diff --git a/ui/js/reducers/file_info.js b/ui/js/reducers/file_info.js
index e462f3cf6..c9e2817b3 100644
--- a/ui/js/reducers/file_info.js
+++ b/ui/js/reducers/file_info.js
@@ -138,39 +138,6 @@ reducers[types.LOADING_VIDEO_FAILED] = function(state, action) {
});
};
-reducers[types.PUBLISH_STARTED] = function(state, action) {
- const { pendingPublish } = action.data;
- const pendingByOutpoint = Object.assign({}, state.pendingByOutpoint);
-
- pendingByOutpoint[pendingPublish.outpoint] = pendingPublish;
-
- return Object.assign({}, state, {
- pendingByOutpoint,
- });
-};
-
-reducers[types.PUBLISH_COMPLETED] = function(state, action) {
- const { pendingPublish } = action.data;
- const pendingByOutpoint = Object.assign({}, state.pendingByOutpoint);
-
- delete pendingByOutpoint[pendingPublish.outpoint];
-
- return Object.assign({}, state, {
- pendingByOutpoint,
- });
-};
-
-reducers[types.PUBLISH_FAILED] = function(state, action) {
- const { pendingPublish } = action.data;
- const pendingByOutpoint = Object.assign({}, state.pendingByOutpoint);
-
- delete pendingByOutpoint[pendingPublish.outpoint];
-
- return Object.assign({}, state, {
- pendingByOutpoint,
- });
-};
-
export default function reducer(state = defaultState, action) {
const handler = reducers[action.type];
if (handler) return handler(state, action);
diff --git a/ui/js/selectors/claims.js b/ui/js/selectors/claims.js
index f0708cf86..3c10ec931 100644
--- a/ui/js/selectors/claims.js
+++ b/ui/js/selectors/claims.js
@@ -110,22 +110,37 @@ export const selectMyClaimsRaw = createSelector(
state => new Set(state.myClaims)
);
+export const selectAbandoningIds = createSelector(_selectState, state =>
+ Object.keys(state.abandoningById || {})
+);
+
+export const selectPendingClaims = createSelector(_selectState, state =>
+ Object.values(state.pendingById || {})
+);
+
export const selectMyClaims = createSelector(
selectMyClaimsRaw,
selectClaimsById,
- (myClaimIds, byId) => {
+ selectAbandoningIds,
+ selectPendingClaims,
+ (myClaimIds, byId, abandoningIds, pendingClaims) => {
const claims = [];
myClaimIds.forEach(id => {
const claim = byId[id];
- if (claim) claims.push(claim);
+ if (claim && abandoningIds.indexOf(id) == -1) claims.push(claim);
});
- return claims;
+ return [...claims, ...pendingClaims];
}
);
+export const selectMyClaimsWithoutChannels = createSelector(
+ selectMyClaims,
+ myClaims => myClaims.filter(claim => !claim.name.match(/^@/))
+);
+
export const selectMyClaimsOutpoints = createSelector(
selectMyClaims,
myClaims => {
diff --git a/ui/js/store.js b/ui/js/store.js
index 35f7ab2ec..124174bc1 100644
--- a/ui/js/store.js
+++ b/ui/js/store.js
@@ -91,17 +91,14 @@ const saveClaimsFilter = createFilter("claims", [
"claimsByUri",
"myClaims",
"myChannelClaims",
-]);
-const saveFileInfosFilter = createFilter("fileInfo", [
- "fileInfos",
- "pendingByOutpoint",
+ "pendingById",
]);
const persistOptions = {
- whitelist: ["claims", "fileInfo"],
+ whitelist: ["claims"],
// Order is important. Needs to be compressed last or other transforms can't
// read the data
- transforms: [saveClaimsFilter, saveFileInfosFilter, compressor],
+ transforms: [saveClaimsFilter, compressor],
debounce: 10000,
storage: localForage,
};
From 76fe44e519635e30cf0d34a2da15a5eb6d1d86da Mon Sep 17 00:00:00 2001
From: 6ea86b96 <6ea86b96@gmail.com>
Date: Mon, 10 Jul 2017 21:44:49 +0700
Subject: [PATCH 15/26] Refactor back to lbry.js localStorage for pending
publishes
---
ui/js/actions/content.js | 61 +++++--------------------------
ui/js/component/fileTile/view.jsx | 9 +----
ui/js/lbry.js | 53 ++++++++++++---------------
ui/js/reducers/claims.js | 39 --------------------
ui/js/store.js | 8 +---
5 files changed, 34 insertions(+), 136 deletions(-)
diff --git a/ui/js/actions/content.js b/ui/js/actions/content.js
index 291dc4d81..fb58d0a74 100644
--- a/ui/js/actions/content.js
+++ b/ui/js/actions/content.js
@@ -389,62 +389,19 @@ export function doCreateChannel(name, amount) {
export function doPublish(params) {
return function(dispatch, getState) {
- let uri;
- const { name, channel_name } = params;
- if (channel_name) {
- uri = lbryuri.build({ name: channel_name, path: name }, false);
- } else {
- uri = lbryuri.build({ name: name }, false);
- }
- const fakeId = "pending";
- const pendingPublish = {
- name,
- channel_name,
- claim_id: fakeId,
- txid: "pending_" + uri,
- nout: 0,
- outpoint: fakeId + ":0",
- time: Date.now(),
- pending: true,
- };
-
- dispatch({
- type: types.PUBLISH_STARTED,
- data: {
- params,
- pendingPublish,
- },
- });
-
return new Promise((resolve, reject) => {
const success = claim => {
- claim.name = params.name;
- claim.channel_name = params.channel_name;
- dispatch({
- type: types.PUBLISH_COMPLETED,
- data: {
- claim,
- uri,
- pendingPublish,
- },
- });
- dispatch(doFileList());
resolve(claim);
- };
- const failure = error => {
- dispatch({
- type: types.PUBLISH_FAILED,
- data: {
- error,
- params,
- uri,
- pendingPublish,
- },
- });
- reject(error);
- };
- lbry.publish(params).then(success, failure);
+ if (claim === true) dispatch(doFetchClaimListMine());
+ else
+ setTimeout(() => dispatch(doFetchClaimListMine()), 20000, {
+ once: true,
+ });
+ };
+ const failure = err => reject(err);
+
+ lbry.publishDeprecated(params, null, success, failure);
});
};
}
diff --git a/ui/js/component/fileTile/view.jsx b/ui/js/component/fileTile/view.jsx
index eb865d1ac..ef4654ce8 100644
--- a/ui/js/component/fileTile/view.jsx
+++ b/ui/js/component/fileTile/view.jsx
@@ -66,14 +66,7 @@ class FileTile extends React.PureComponent {
? metadata.title
: lbryuri.parse(uri).contentName;
const obscureNsfw = this.props.obscureNsfw && metadata && metadata.nsfw;
- let onClick;
- if (isClaimed) {
- onClick = () => navigate("/show", { uri });
- } else {
- onClick = () => {
- return false;
- };
- }
+ let onClick = () => navigate("/show", { uri });
let description = "";
if (isClaimed) {
diff --git a/ui/js/lbry.js b/ui/js/lbry.js
index f13665e09..7409bdde4 100644
--- a/ui/js/lbry.js
+++ b/ui/js/lbry.js
@@ -223,45 +223,38 @@ lbry.publishDeprecated = function(
) {
lbry.publish(params).then(
result => {
- if (returnedPending) {
- return;
- }
-
- clearTimeout(returnPendingTimeout);
+ if (returnPendingTimeout) clearTimeout(returnPendingTimeout);
publishedCallback(result);
},
err => {
- if (returnedPending) {
- return;
- }
-
- clearTimeout(returnPendingTimeout);
+ if (returnPendingTimeout) clearTimeout(returnPendingTimeout);
errorCallback(err);
}
);
- let returnedPending = false;
// Give a short grace period in case publish() returns right away or (more likely) gives an error
- const returnPendingTimeout = setTimeout(() => {
- returnedPending = true;
+ const returnPendingTimeout = setTimeout(
+ () => {
+ if (publishedCallback) {
+ savePendingPublish({
+ name: params.name,
+ channel_name: params.channel_name,
+ });
+ publishedCallback(true);
+ }
- if (publishedCallback) {
- savePendingPublish({
- name: params.name,
- channel_name: params.channel_name,
- });
- publishedCallback(true);
- }
-
- if (fileListedCallback) {
- const { name, channel_name } = params;
- savePendingPublish({
- name: params.name,
- channel_name: params.channel_name,
- });
- fileListedCallback(true);
- }
- }, 2000);
+ if (fileListedCallback) {
+ const { name, channel_name } = params;
+ savePendingPublish({
+ name: params.name,
+ channel_name: params.channel_name,
+ });
+ fileListedCallback(true);
+ }
+ },
+ 2000,
+ { once: true }
+ );
};
lbry.getClientSettings = function() {
diff --git a/ui/js/reducers/claims.js b/ui/js/reducers/claims.js
index 8f60bf9a1..2470e5f9c 100644
--- a/ui/js/reducers/claims.js
+++ b/ui/js/reducers/claims.js
@@ -159,45 +159,6 @@ reducers[types.CREATE_CHANNEL_COMPLETED] = function(state, action) {
});
};
-reducers[types.PUBLISH_STARTED] = function(state, action) {
- const { pendingPublish } = action.data;
- const pendingById = Object.assign({}, state.pendingById);
-
- pendingById[pendingPublish.claim_id] = pendingPublish;
-
- return Object.assign({}, state, {
- pendingById,
- });
-};
-
-reducers[types.PUBLISH_COMPLETED] = function(state, action) {
- const { claim, pendingPublish } = action.data;
- const byId = Object.assign({}, state.byId);
- const myClaims = new Set(state.myClaims);
- const pendingById = Object.assign({}, state.pendingById);
-
- byId[claim.claim_id] = claim;
- myClaims.add(claim.claim_id);
- delete pendingById[pendingPublish.claim_id];
-
- return Object.assign({}, state, {
- byId,
- myClaims,
- pendingById,
- });
-};
-
-reducers[types.PUBLISH_FAILED] = function(state, action) {
- const { pendingPublish } = action.data;
- const pendingById = Object.assign({}, state.pendingById);
-
- delete pendingById[pendingPublish.claim_id];
-
- return Object.assign({}, state, {
- pendingById,
- });
-};
-
export default function reducer(state = defaultState, action) {
const handler = reducers[action.type];
if (handler) return handler(state, action);
diff --git a/ui/js/store.js b/ui/js/store.js
index 124174bc1..5eb84d4cf 100644
--- a/ui/js/store.js
+++ b/ui/js/store.js
@@ -86,13 +86,7 @@ const createStoreWithMiddleware = redux.compose(
const reduxStore = createStoreWithMiddleware(enableBatching(reducers));
const compressor = createCompressor();
-const saveClaimsFilter = createFilter("claims", [
- "byId",
- "claimsByUri",
- "myClaims",
- "myChannelClaims",
- "pendingById",
-]);
+const saveClaimsFilter = createFilter("claims", ["byId", "claimsByUri"]);
const persistOptions = {
whitelist: ["claims"],
From 443caa27406311fc8063e062a2f53d2fac61624e Mon Sep 17 00:00:00 2001
From: 6ea86b96 <6ea86b96@gmail.com>
Date: Tue, 11 Jul 2017 13:01:44 +0700
Subject: [PATCH 16/26] Fix promise reject when creating a channel
---
ui/js/actions/content.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ui/js/actions/content.js b/ui/js/actions/content.js
index fb58d0a74..f46a1fa33 100644
--- a/ui/js/actions/content.js
+++ b/ui/js/actions/content.js
@@ -380,7 +380,7 @@ export function doCreateChannel(name, amount) {
resolve(channelClaim);
},
err => {
- resolve(err);
+ reject(err);
}
);
});
From 6f336c96c5a89e82d42f886964ab3fab49567272 Mon Sep 17 00:00:00 2001
From: 6ea86b96 <6ea86b96@gmail.com>
Date: Tue, 11 Jul 2017 13:15:51 +0700
Subject: [PATCH 17/26] Remove unnecessary binds
---
ui/js/component/publishForm/internal/channelSection.jsx | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/ui/js/component/publishForm/internal/channelSection.jsx b/ui/js/component/publishForm/internal/channelSection.jsx
index 274f30bdf..6c7802625 100644
--- a/ui/js/component/publishForm/internal/channelSection.jsx
+++ b/ui/js/component/publishForm/internal/channelSection.jsx
@@ -69,22 +69,22 @@ class ChannelSection extends React.PureComponent {
this.setState({
creatingChannel: true,
});
- const success = (() => {
+ const success = () => {
this.setState({
creatingChannel: false,
addingChannel: false,
channel: newChannelName,
});
this.props.handleChannelChange(newChannelName);
- }).bind(this);
- const failure = (err => {
+ };
+ const failure = err => {
this.setState({
creatingChannel: false,
});
this.refs.newChannelName.showError(
__("Unable to create channel due to an internal error.")
);
- }).bind(this);
+ };
this.props.createChannel(newChannelName, amount).then(success, failure);
}
From d3c621ed1265645cf18cbea7bb10ee8801a71fe1 Mon Sep 17 00:00:00 2001
From: 6ea86b96 <6ea86b96@gmail.com>
Date: Tue, 11 Jul 2017 14:44:45 +0700
Subject: [PATCH 18/26] use _SUCCEEDED for abandom claim and file list action
types
---
ui/js/actions/file_info.js | 4 ++--
ui/js/constants/action_types.js | 4 ++--
ui/js/reducers/claims.js | 2 +-
ui/js/reducers/file_info.js | 2 +-
4 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/ui/js/actions/file_info.js b/ui/js/actions/file_info.js
index eb4a6753f..b36cb784e 100644
--- a/ui/js/actions/file_info.js
+++ b/ui/js/actions/file_info.js
@@ -57,7 +57,7 @@ export function doFileList() {
lbry.file_list().then(fileInfos => {
dispatch({
- type: types.FILE_LIST_COMPLETED,
+ type: types.FILE_LIST_SUCCEEDED,
data: {
fileInfos,
},
@@ -107,7 +107,7 @@ export function doDeleteFile(outpoint, deleteFromComputer, abandonClaim) {
const success = setTimeout(
() => {
dispatch({
- type: types.ABANDON_CLAIM_COMPLETED,
+ type: types.ABANDON_CLAIM_SUCCEEDED,
data: {
claimId: fileInfo.claim_id,
},
diff --git a/ui/js/constants/action_types.js b/ui/js/constants/action_types.js
index 457761441..7d38568f3 100644
--- a/ui/js/constants/action_types.js
+++ b/ui/js/constants/action_types.js
@@ -47,7 +47,7 @@ export const FETCH_CLAIM_LIST_MINE_STARTED = "FETCH_CLAIM_LIST_MINE_STARTED";
export const FETCH_CLAIM_LIST_MINE_COMPLETED =
"FETCH_CLAIM_LIST_MINE_COMPLETED";
export const FILE_LIST_STARTED = "FILE_LIST_STARTED";
-export const FILE_LIST_COMPLETED = "FILE_LIST_COMPLETED";
+export const FILE_LIST_SUCCEEDED = "FILE_LIST_SUCCEEDED";
export const FETCH_FILE_INFO_STARTED = "FETCH_FILE_INFO_STARTED";
export const FETCH_FILE_INFO_COMPLETED = "FETCH_FILE_INFO_COMPLETED";
export const FETCH_COST_INFO_STARTED = "FETCH_COST_INFO_STARTED";
@@ -63,7 +63,7 @@ export const FETCH_AVAILABILITY_STARTED = "FETCH_AVAILABILITY_STARTED";
export const FETCH_AVAILABILITY_COMPLETED = "FETCH_AVAILABILITY_COMPLETED";
export const FILE_DELETE = "FILE_DELETE";
export const ABANDON_CLAIM_STARTED = "ABANDON_CLAIM_STARTED";
-export const ABANDON_CLAIM_COMPLETED = "ABANDON_CLAIM_COMPLETED";
+export const ABANDON_CLAIM_SUCCEEDED = "ABANDON_CLAIM_SUCCEEDED";
export const FETCH_CHANNEL_LIST_MINE_STARTED =
"FETCH_CHANNEL_LIST_MINE_STARTED";
export const FETCH_CHANNEL_LIST_MINE_COMPLETED =
diff --git a/ui/js/reducers/claims.js b/ui/js/reducers/claims.js
index 2470e5f9c..adc0eb2ed 100644
--- a/ui/js/reducers/claims.js
+++ b/ui/js/reducers/claims.js
@@ -122,7 +122,7 @@ reducers[types.ABANDON_CLAIM_STARTED] = function(state, action) {
});
};
-reducers[types.ABANDON_CLAIM_COMPLETED] = function(state, action) {
+reducers[types.ABANDON_CLAIM_SUCCEEDED] = function(state, action) {
const { claimId } = action.data;
const myClaims = new Set(state.myClaims);
const byId = Object.assign({}, state.byId);
diff --git a/ui/js/reducers/file_info.js b/ui/js/reducers/file_info.js
index c9e2817b3..500fbdf82 100644
--- a/ui/js/reducers/file_info.js
+++ b/ui/js/reducers/file_info.js
@@ -10,7 +10,7 @@ reducers[types.FILE_LIST_STARTED] = function(state, action) {
});
};
-reducers[types.FILE_LIST_COMPLETED] = function(state, action) {
+reducers[types.FILE_LIST_SUCCEEDED] = function(state, action) {
const { fileInfos } = action.data;
const newByOutpoint = Object.assign({}, state.byOutpoint);
const pendingByOutpoint = Object.assign({}, state.pendingByOutpoint);
From d0e3dd8f990a438e3177f6b770d58da017b0d11f Mon Sep 17 00:00:00 2001
From: 6ea86b96 <6ea86b96@gmail.com>
Date: Tue, 11 Jul 2017 14:57:56 +0700
Subject: [PATCH 19/26] Extract TruncatedMarkdown component
---
ui/js/component/common.js | 36 --------------------
ui/js/component/fileCard/view.jsx | 8 ++---
ui/js/component/truncatedMarkdown/index.js | 5 +++
ui/js/component/truncatedMarkdown/view.jsx | 39 ++++++++++++++++++++++
4 files changed, 46 insertions(+), 42 deletions(-)
create mode 100644 ui/js/component/truncatedMarkdown/index.js
create mode 100644 ui/js/component/truncatedMarkdown/view.jsx
diff --git a/ui/js/component/common.js b/ui/js/component/common.js
index 57314bdd3..38dbf83fd 100644
--- a/ui/js/component/common.js
+++ b/ui/js/component/common.js
@@ -1,7 +1,5 @@
import React from "react";
-import ReactDOMServer from "react-dom/server";
import lbry from "../lbry.js";
-import ReactMarkdown from "react-markdown";
//component/icon.js
export class Icon extends React.PureComponent {
@@ -44,40 +42,6 @@ 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(
-
- );
- 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 (
-
- {content}
-
- );
- }
-}
-
export class BusyMessage extends React.PureComponent {
static propTypes = {
message: React.PropTypes.string,
diff --git a/ui/js/component/fileCard/view.jsx b/ui/js/component/fileCard/view.jsx
index 256bc9014..c6e179af7 100644
--- a/ui/js/component/fileCard/view.jsx
+++ b/ui/js/component/fileCard/view.jsx
@@ -1,15 +1,11 @@
import React from "react";
import lbryuri from "lbryuri.js";
import Link from "component/link";
-import {
- Thumbnail,
- TruncatedText,
- Icon,
- TruncatedMarkdown,
-} from "component/common";
+import { Thumbnail, TruncatedText, Icon } from "component/common";
import FilePrice from "component/filePrice";
import UriIndicator from "component/uriIndicator";
import NsfwOverlay from "component/nsfwOverlay";
+import TruncatedMarkdown from "component/truncatedMarkdown";
class FileCard extends React.PureComponent {
constructor(props) {
diff --git a/ui/js/component/truncatedMarkdown/index.js b/ui/js/component/truncatedMarkdown/index.js
new file mode 100644
index 000000000..7cec6defe
--- /dev/null
+++ b/ui/js/component/truncatedMarkdown/index.js
@@ -0,0 +1,5 @@
+import React from "react";
+import { connect } from "react-redux";
+import TruncatedMarkdown from "./view";
+
+export default connect()(TruncatedMarkdown);
diff --git a/ui/js/component/truncatedMarkdown/view.jsx b/ui/js/component/truncatedMarkdown/view.jsx
new file mode 100644
index 000000000..59e42d6af
--- /dev/null
+++ b/ui/js/component/truncatedMarkdown/view.jsx
@@ -0,0 +1,39 @@
+import React from "react";
+import ReactMarkdown from "react-markdown";
+import ReactDOMServer from "react-dom/server";
+
+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(
+
+ );
+ 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 (
+
+ {content}
+
+ );
+ }
+}
+
+export default TruncatedMarkdown;
From f1c45775ab33b48f88eed0b41bd872035c7e7ecf Mon Sep 17 00:00:00 2001
From: 6ea86b96 <6ea86b96@gmail.com>
Date: Tue, 11 Jul 2017 15:30:28 +0700
Subject: [PATCH 20/26] Cleaner way of filtering published claims while
abandoning
---
ui/js/actions/file_info.js | 18 +++++-------------
ui/js/reducers/claims.js | 8 ++++++--
2 files changed, 11 insertions(+), 15 deletions(-)
diff --git a/ui/js/actions/file_info.js b/ui/js/actions/file_info.js
index b36cb784e..d21df8d2a 100644
--- a/ui/js/actions/file_info.js
+++ b/ui/js/actions/file_info.js
@@ -102,20 +102,12 @@ export function doDeleteFile(outpoint, deleteFromComputer, abandonClaim) {
},
});
- // We need to run this after a few seconds or the claim gets added back
- // to the store again by an already running fetch claims query.
- const success = setTimeout(
- () => {
- dispatch({
- type: types.ABANDON_CLAIM_SUCCEEDED,
- data: {
- claimId: fileInfo.claim_id,
- },
- });
+ const success = dispatch({
+ type: types.ABANDON_CLAIM_SUCCEEDED,
+ data: {
+ claimId: fileInfo.claim_id,
},
- 10000,
- { once: true }
- );
+ });
lbry.claim_abandon({ claim_id: fileInfo.claim_id }).then(success);
}
}
diff --git a/ui/js/reducers/claims.js b/ui/js/reducers/claims.js
index adc0eb2ed..9e072f1ce 100644
--- a/ui/js/reducers/claims.js
+++ b/ui/js/reducers/claims.js
@@ -43,8 +43,12 @@ reducers[types.FETCH_CLAIM_LIST_MINE_COMPLETED] = function(state, action) {
const byUri = Object.assign({}, state.claimsByUri);
const byId = Object.assign({}, state.byId);
const pendingById = Object.assign({}, state.pendingById);
-
- const myClaims = new Set(claims.map(claim => claim.claim_id));
+ const abandoningById = Object.assign({}, state.abandoningById);
+ const myClaims = new Set(
+ claims
+ .map(claim => claim.claim_id)
+ .filter(claimId => Object.keys(abandoningById).indexOf(claimId) === -1)
+ );
claims.forEach(claim => {
byId[claim.claim_id] = claim;
From 103e4302d46c5a05dbc905378c51605f9a1b162e Mon Sep 17 00:00:00 2001
From: 6ea86b96 <6ea86b96@gmail.com>
Date: Wed, 12 Jul 2017 13:36:08 +0700
Subject: [PATCH 21/26] Disable publish button if bid is too low
---
ui/js/component/publishForm/view.jsx | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/ui/js/component/publishForm/view.jsx b/ui/js/component/publishForm/view.jsx
index 446081f6d..95abbaa96 100644
--- a/ui/js/component/publishForm/view.jsx
+++ b/ui/js/component/publishForm/view.jsx
@@ -876,7 +876,10 @@ class PublishForm extends React.PureComponent {
disabled={
this.state.submitting ||
(this.state.uri &&
- this.props.resolvingUris.indexOf(this.state.uri) !== -1)
+ this.props.resolvingUris.indexOf(this.state.uri) !== -1) ||
+ (this.claim() &&
+ !this.topClaimIsMine() &&
+ this.state.bid <= this.topClaimValue())
}
/>
Date: Wed, 12 Jul 2017 14:05:10 +0700
Subject: [PATCH 22/26] Drop notice component
---
ui/js/component/notice.js | 27 ---------------------------
ui/js/component/publishForm/view.jsx | 13 ++++---------
2 files changed, 4 insertions(+), 36 deletions(-)
delete mode 100644 ui/js/component/notice.js
diff --git a/ui/js/component/notice.js b/ui/js/component/notice.js
deleted file mode 100644
index 623ed51ec..000000000
--- a/ui/js/component/notice.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import React from "react";
-
-export class Notice extends React.PureComponent {
- static propTypes = {
- isError: React.PropTypes.bool,
- };
-
- static defaultProps = {
- isError: false,
- };
-
- render() {
- return (
-
- {this.props.children}
-
- );
- }
-}
-
-export default Notice;
diff --git a/ui/js/component/publishForm/view.jsx b/ui/js/component/publishForm/view.jsx
index 95abbaa96..26eadf74b 100644
--- a/ui/js/component/publishForm/view.jsx
+++ b/ui/js/component/publishForm/view.jsx
@@ -4,7 +4,6 @@ import lbryuri from "lbryuri";
import { FormField, FormRow } from "component/form.js";
import Link from "component/link";
import Modal from "component/modal";
-import Notice from "component/notice";
import { BusyMessage } from "component/common";
import ChannelSection from "./internal/ChannelSection";
@@ -190,7 +189,7 @@ class PublishForm extends React.PureComponent {
if (!claim) return true;
if (!myClaimInfo) return false;
- return myClaimInfo.amount >= claimInfo.amount;
+ return myClaimInfo.amount >= claim.amount;
}
myClaimInfo() {
@@ -453,11 +452,7 @@ class PublishForm extends React.PureComponent {
getNameBidHelpText() {
if (this.state.prefillDone) {
- return (
-
- {__("Existing claim data was prefilled")}
-
- );
+ return __("Existing claim data was prefilled");
}
if (
@@ -472,13 +467,13 @@ class PublishForm extends React.PureComponent {
return __("This URL is unused.");
} else if (this.myClaimExists() && !this.state.prefillDone) {
return (
-
+
{__("You already have a claim with this name.")}{" "}
this.handlePrefillClicked()}
/>
-
+
);
} else if (this.claim()) {
if (this.topClaimValue() === 1) {
From cd9fcf550aaf7642e66ee49ed1b8cc0680bbba88 Mon Sep 17 00:00:00 2001
From: 6ea86b96 <6ea86b96@gmail.com>
Date: Wed, 12 Jul 2017 14:05:44 +0700
Subject: [PATCH 23/26] Remove console.log
---
ui/js/component/publishForm/view.jsx | 1 -
1 file changed, 1 deletion(-)
diff --git a/ui/js/component/publishForm/view.jsx b/ui/js/component/publishForm/view.jsx
index 26eadf74b..61ef71b1c 100644
--- a/ui/js/component/publishForm/view.jsx
+++ b/ui/js/component/publishForm/view.jsx
@@ -292,7 +292,6 @@ class PublishForm extends React.PureComponent {
newState.licenseType = licenseType;
}
- console.log(newState);
this.setState(newState);
}
From 0419399decd3a8b19e5dc47ba39af985629c42a0 Mon Sep 17 00:00:00 2001
From: 6ea86b96 <6ea86b96@gmail.com>
Date: Wed, 12 Jul 2017 14:28:28 +0700
Subject: [PATCH 24/26] Disable markdown editor side-by-side mode
---
ui/js/component/form.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ui/js/component/form.js b/ui/js/component/form.js
index 6a65218e4..805afac70 100644
--- a/ui/js/component/form.js
+++ b/ui/js/component/form.js
@@ -44,7 +44,7 @@ export class FormField extends React.PureComponent {
this._element = SimpleMDE;
this._type = "textarea";
this._extraElementProps.options = {
- hideIcons: ["guide", "heading", "image", "fullscreen"],
+ hideIcons: ["guide", "heading", "image", "fullscreen", "side-by-side"],
};
} else if (formFieldFileSelectorTypes.includes(this.props.type)) {
this._element = "input";
From d1eb8f5de375d91e03f77ef5afaf694a782dc2ec Mon Sep 17 00:00:00 2001
From: 6ea86b96 <6ea86b96@gmail.com>
Date: Wed, 12 Jul 2017 15:08:59 +0700
Subject: [PATCH 25/26] Stop old file infos from updated claims appearing in
downloaded file list
---
CHANGELOG.md | 1 +
ui/js/component/fileList/view.jsx | 2 +-
ui/js/page/fileListDownloaded/index.js | 8 ++++++++
ui/js/page/fileListDownloaded/view.jsx | 1 +
ui/js/selectors/file_info.js | 19 +++++++++----------
5 files changed, 20 insertions(+), 11 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d8f65e2ee..0dd35a53c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,6 +21,7 @@ Web UI version numbers should always match the corresponding version of LBRY App
* Fixed bug with download notice when switching window focus
* Fixed newly published files appearing twice
* Fixed unconfirmed published files missing channel name
+ * Fixed old files from updated published claims appearing in downloaded list
### Deprecated
*
diff --git a/ui/js/component/fileList/view.jsx b/ui/js/component/fileList/view.jsx
index edd64b993..f910e9500 100644
--- a/ui/js/component/fileList/view.jsx
+++ b/ui/js/component/fileList/view.jsx
@@ -81,7 +81,7 @@ class FileList extends React.PureComponent {
content.push(
({
fileInfos: selectFileInfosDownloaded(state),
isFetching: selectIsFetchingFileListDownloadedOrPublished(state),
+ claims: selectMyClaimsWithoutChannels(state),
+ isFetchingClaims: selectIsFetchingClaimListMine(state),
});
const perform = dispatch => ({
@@ -19,6 +26,7 @@ const perform = dispatch => ({
fetchFileInfosDownloaded: () =>
dispatch(doFetchFileInfosAndPublishedClaims()),
cancelResolvingUris: () => dispatch(doCancelAllResolvingUris()),
+ fetchClaims: () => dispatch(doFetchClaimListMine()),
});
export default connect(select, perform)(FileListDownloaded);
diff --git a/ui/js/page/fileListDownloaded/view.jsx b/ui/js/page/fileListDownloaded/view.jsx
index c1501ec78..8eb18e9d5 100644
--- a/ui/js/page/fileListDownloaded/view.jsx
+++ b/ui/js/page/fileListDownloaded/view.jsx
@@ -12,6 +12,7 @@ import SubHeader from "component/subHeader";
class FileListDownloaded extends React.PureComponent {
componentWillMount() {
+ if (!this.props.isFetchingClaims) this.props.fetchClaims();
if (!this.props.isFetching) this.props.fetchFileInfosDownloaded();
}
diff --git a/ui/js/selectors/file_info.js b/ui/js/selectors/file_info.js
index 7b8a10769..ef469ab02 100644
--- a/ui/js/selectors/file_info.js
+++ b/ui/js/selectors/file_info.js
@@ -3,6 +3,7 @@ import { createSelector } from "reselect";
import {
selectClaimsByUri,
selectIsFetchingClaimListMine,
+ selectMyClaims,
selectMyClaimsOutpoints,
} from "selectors/claims";
@@ -76,19 +77,17 @@ export const selectFileInfosPendingPublish = createSelector(
export const selectFileInfosDownloaded = createSelector(
selectFileInfosByOutpoint,
- selectMyClaimsOutpoints,
- (byOutpoint, myClaimOutpoints) => {
- const fileInfoList = [];
- Object.values(byOutpoint).forEach(fileInfo => {
- if (
+ selectMyClaims,
+ (byOutpoint, myClaims) => {
+ return Object.values(byOutpoint).filter(fileInfo => {
+ const myClaimIds = myClaims.map(claim => claim.claim_id);
+
+ return (
fileInfo &&
- myClaimOutpoints.indexOf(fileInfo.outpoint) === -1 &&
+ myClaimIds.indexOf(fileInfo.claim_id) === -1 &&
(fileInfo.completed || fileInfo.written_bytes)
- ) {
- fileInfoList.push(fileInfo);
- }
+ );
});
- return fileInfoList;
}
);
From 3950f7e9b24d3eae66862665ad1d679f4bd241f9 Mon Sep 17 00:00:00 2001
From: Jeremy Kauffman
Date: Thu, 13 Jul 2017 10:19:27 -0400
Subject: [PATCH 26/26] fix import case
---
ui/js/component/publishForm/view.jsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ui/js/component/publishForm/view.jsx b/ui/js/component/publishForm/view.jsx
index 61ef71b1c..da6e6eb39 100644
--- a/ui/js/component/publishForm/view.jsx
+++ b/ui/js/component/publishForm/view.jsx
@@ -5,7 +5,7 @@ import { FormField, FormRow } from "component/form.js";
import Link from "component/link";
import Modal from "component/modal";
import { BusyMessage } from "component/common";
-import ChannelSection from "./internal/ChannelSection";
+import ChannelSection from "./internal/channelSection";
class PublishForm extends React.PureComponent {
constructor(props) {