significant additional form changes and cleanup

This commit is contained in:
Jeremy Kauffman 2017-08-06 18:24:55 -04:00
parent 9c3d63353d
commit a299ca2ddd
20 changed files with 428 additions and 269 deletions

1
.gitignore vendored
View file

@ -13,6 +13,7 @@
/app/node_modules
/build/venv
/lbry-app-venv
/lbry-app
/lbry-venv
/daemon/build
/daemon/venv

View file

@ -8,15 +8,16 @@ Web UI version numbers should always match the corresponding version of LBRY App
## [Unreleased]
### Added
* Added a new component, "PriceForm" which is now used in Publish and Settings
* Added a new component, `FormFieldPrice` which is now used in Publish and Settings
*
### Changed
*
* Some form field refactoring as we progress towards form sanity.
*
### Fixed
* Tiles will no longer be blurry on hover (Windows only bug)
* Removed placeholder values from price selection form fields, which was causing confusion that these were real values (#426)
*
### Deprecated
@ -24,7 +25,7 @@ Web UI version numbers should always match the corresponding version of LBRY App
*
### Removed
* Removed one instance of string "Max Purchase Price" from settings page, it's redudant.
* Removed the label "Max Purchase Price" from settings page. It was redundant.
*
## [0.14.3] - 2017-08-03

View file

@ -57,8 +57,8 @@
},
"devDependencies": {
"devtron": "^1.4.0",
"electron": "^1.4.15",
"electron": "^1.7.5",
"electron-builder": "^11.7.0",
"electron-debug": "^1.1.0"
"electron-debug": "^1.4.0"
}
}

View file

@ -2,7 +2,7 @@ import React from "react";
import lbry from "lbry.js";
import lbryuri from "lbryuri.js";
import Link from "component/link";
import { FormField } from "component/form.js";
import FormField from "component/formField";
import FileTile from "component/fileTile";
import rewards from "rewards.js";
import lbryio from "lbryio.js";

View file

@ -1,183 +1,14 @@
import React from "react";
import FileSelector from "./file-selector.js";
import SimpleMDE from "react-simplemde-editor";
import style from "react-simplemde-editor/dist/simplemde.min.css";
import FormField from "component/formField";
let formFieldCounter = 0,
formFieldFileSelectorTypes = ["file", "directory"],
formFieldNestedLabelTypes = ["radio", "checkbox"];
let formFieldCounter = 0;
function formFieldId() {
export const formFieldNestedLabelTypes = ["radio", "checkbox"];
export function formFieldId() {
return "form-field-" + ++formFieldCounter;
}
export class FormField extends React.PureComponent {
static propTypes = {
type: React.PropTypes.string.isRequired,
prefix: React.PropTypes.string,
postfix: React.PropTypes.string,
hasError: React.PropTypes.bool,
};
constructor(props) {
super(props);
this._fieldRequiredText = __("This field is required");
this._type = null;
this._element = null;
this._extraElementProps = {};
this.state = {
isError: null,
errorMessage: null,
};
}
componentWillMount() {
if (["text", "number", "radio", "checkbox"].includes(this.props.type)) {
this._element = "input";
this._type = this.props.type;
} 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", "side-by-side"],
};
} else if (formFieldFileSelectorTypes.includes(this.props.type)) {
this._element = "input";
this._type = "hidden";
} else {
// Non <input> field, e.g. <select>, <textarea>
this._element = this.props.type;
}
}
componentDidMount() {
/**
* We have to add the webkitdirectory attribute here because React doesn't allow it in JSX
* https://github.com/facebook/react/issues/3468
*/
if (this.props.type == "directory") {
this.refs.field.webkitdirectory = true;
}
}
handleFileChosen(path) {
this.refs.field.value = path;
if (this.props.onChange) {
// Updating inputs programmatically doesn't generate an event, so we have to make our own
const event = new Event("change", { bubbles: true });
this.refs.field.dispatchEvent(event); // This alone won't generate a React event, but we use it to attach the field as a target
this.props.onChange(event);
}
}
showError(text) {
this.setState({
isError: true,
errorMessage: text,
});
}
focus() {
this.refs.field.focus();
}
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;
}
}
getSelectedElement() {
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),
isError = this.state.isError !== null
? this.state.isError
: this.props.hasError,
elementId = this.props.id ? this.props.id : formFieldId(),
renderElementInsideLabel =
this.props.label && formFieldNestedLabelTypes.includes(this.props.type);
delete otherProps.type;
delete otherProps.label;
delete otherProps.hasError;
delete otherProps.className;
delete otherProps.postfix;
delete otherProps.prefix;
const element = (
<this._element
id={elementId}
type={this._type}
name={this.props.name}
ref="field"
placeholder={this.props.placeholder}
className={
"form-field__input form-field__input-" +
this.props.type +
" " +
(this.props.className || "") +
(isError ? "form-field__input--error" : "")
}
{...otherProps}
{...this._extraElementProps}
>
{this.props.children}
</this._element>
);
return (
<div className={"form-field form-field--" + this.props.type}>
{this.props.prefix
? <span className="form-field__prefix">{this.props.prefix}</span>
: ""}
{renderElementInsideLabel
? <label
htmlFor={elementId}
className={
"form-field__label " +
(isError ? "form-field__label--error" : "")
}
>
{element}
{this.props.label}
</label>
: element}
{formFieldFileSelectorTypes.includes(this.props.type)
? <FileSelector
type={this.props.type}
onFileChosen={this.handleFileChosen.bind(this)}
{...(this.props.defaultValue
? { initPath: this.props.defaultValue }
: {})}
/>
: null}
{this.props.postfix
? <span className="form-field__postfix">{this.props.postfix}</span>
: ""}
{isError && this.state.errorMessage
? <div className="form-field__error">{this.state.errorMessage}</div>
: ""}
</div>
);
}
}
export class FormRow extends React.PureComponent {
static propTypes = {
label: React.PropTypes.oneOfType([
@ -194,6 +25,8 @@ export class FormRow extends React.PureComponent {
constructor(props) {
super(props);
this._field = null;
this._fieldRequiredText = __("This field is required");
this.state = this.getStateFromProps(props);
@ -233,19 +66,24 @@ export class FormRow extends React.PureComponent {
}
getValue() {
return this.refs.field.getValue();
return this._field.getValue();
}
getSelectedElement() {
return this.refs.field.getSelectedElement();
return this._field.getSelectedElement();
}
getOptions() {
return this.refs.field.getOptions();
if (!this._field || !this._field.getOptions) {
console.log(this);
console.log(this._field);
console.log(this._field.getOptions);
}
return this._field.getOptions();
}
focus() {
this.refs.field.focus();
this._field.focus();
}
render() {
@ -281,7 +119,13 @@ export class FormRow extends React.PureComponent {
</label>
</div>
: ""}
<FormField ref="field" hasError={this.state.isError} {...fieldProps} />
<FormField
ref={ref => {
this._field = ref ? ref.getWrappedInstance() : null;
}}
hasError={this.state.isError}
{...fieldProps}
/>
{!this.state.isError && this.props.helper
? <div className="form-field__helper">{this.props.helper}</div>
: ""}

View file

@ -0,0 +1,5 @@
import React from "react";
import { connect } from "react-redux";
import FormField from "./view";
export default connect(null, null, null, { withRef: true })(FormField);

View file

@ -0,0 +1,178 @@
import React from "react";
import FileSelector from "component/file-selector.js";
import SimpleMDE from "react-simplemde-editor";
import { formFieldNestedLabelTypes, formFieldId } from "../form";
import style from "react-simplemde-editor/dist/simplemde.min.css";
const formFieldFileSelectorTypes = ["file", "directory"];
class FormField extends React.PureComponent {
static propTypes = {
type: React.PropTypes.string.isRequired,
prefix: React.PropTypes.string,
postfix: React.PropTypes.string,
hasError: React.PropTypes.bool,
};
constructor(props) {
super(props);
this._fieldRequiredText = __("This field is required");
this._type = null;
this._element = null;
this._extraElementProps = {};
this.state = {
isError: null,
errorMessage: null,
};
}
componentWillMount() {
if (["text", "number", "radio", "checkbox"].includes(this.props.type)) {
this._element = "input";
this._type = this.props.type;
} 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", "side-by-side"],
};
} else if (formFieldFileSelectorTypes.includes(this.props.type)) {
this._element = "input";
this._type = "hidden";
} else {
// Non <input> field, e.g. <select>, <textarea>
this._element = this.props.type;
}
}
componentDidMount() {
/**
* We have to add the webkitdirectory attribute here because React doesn't allow it in JSX
* https://github.com/facebook/react/issues/3468
*/
if (this.props.type == "directory") {
this.refs.field.webkitdirectory = true;
}
}
handleFileChosen(path) {
this.refs.field.value = path;
if (this.props.onChange) {
// Updating inputs programmatically doesn't generate an event, so we have to make our own
const event = new Event("change", { bubbles: true });
this.refs.field.dispatchEvent(event); // This alone won't generate a React event, but we use it to attach the field as a target
this.props.onChange(event);
}
}
showError(text) {
this.setState({
isError: true,
errorMessage: text,
});
}
focus() {
this.refs.field.focus();
}
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;
}
}
getSelectedElement() {
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),
isError = this.state.isError !== null
? this.state.isError
: this.props.hasError,
elementId = this.props.elementId ? this.props.elementId : formFieldId(),
renderElementInsideLabel =
this.props.label && formFieldNestedLabelTypes.includes(this.props.type);
delete otherProps.type;
delete otherProps.label;
delete otherProps.hasError;
delete otherProps.className;
delete otherProps.postfix;
delete otherProps.prefix;
delete otherProps.dispatch;
const element = (
<this._element
id={elementId}
type={this._type}
name={this.props.name}
ref="field"
placeholder={this.props.placeholder}
className={
"form-field__input form-field__input-" +
this.props.type +
" " +
(this.props.className || "") +
(isError ? "form-field__input--error" : "")
}
{...otherProps}
{...this._extraElementProps}
>
{this.props.children}
</this._element>
);
return (
<div className={"form-field form-field--" + this.props.type}>
{this.props.prefix
? <span className="form-field__prefix">{this.props.prefix}</span>
: ""}
{renderElementInsideLabel
? <label
htmlFor={elementId}
className={
"form-field__label " +
(isError ? "form-field__label--error" : "")
}
>
{element}
{this.props.label}
</label>
: element}
{formFieldFileSelectorTypes.includes(this.props.type)
? <FileSelector
type={this.props.type}
onFileChosen={this.handleFileChosen.bind(this)}
{...(this.props.defaultValue
? { initPath: this.props.defaultValue }
: {})}
/>
: null}
{this.props.postfix
? <span className="form-field__postfix">{this.props.postfix}</span>
: ""}
{isError && this.state.errorMessage
? <div className="form-field__error">{this.state.errorMessage}</div>
: ""}
</div>
);
}
}
export default FormField;

View file

@ -0,0 +1,5 @@
import React from "react";
import { connect } from "react-redux";
import FormFieldPrice from "./view";
export default connect(null, null)(FormFieldPrice);

View file

@ -0,0 +1,68 @@
import React from "react";
import FormField from "component/formField";
class FormFieldPrice extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
amount: props.defaultValue && props.defaultValue.amount
? props.defaultValue.amount
: "",
currency: props.defaultValue && props.defaultValue.currency
? props.defaultValue.currency
: "LBC",
};
}
dispatchChange() {
this.props.onChange({
amount: this.state.amount,
currency: this.state.currency,
});
}
handleFeeAmountChange(event) {
this.state.amount = event.target.value ? Number(event.target.value) : null;
this.dispatchChange();
}
handleFeeCurrencyChange(event) {
this.state.currency = event.target.value;
this.dispatchChange();
}
render() {
const { defaultValue, placeholder, min, step } = this.props;
return (
<span className="form-field">
<FormField
type="number"
name="amount"
min={min}
placeholder={placeholder || null}
step={step}
onChange={event => this.handleFeeAmountChange(event)}
defaultValue={
defaultValue && defaultValue.amount ? defaultValue.amount : ""
}
className="form-field__input--inline"
/>
<FormField
type="select"
name="currency"
onChange={event => this.handleFeeCurrencyChange(event)}
defaultValue={
defaultValue && defaultValue.currency ? defaultValue.currency : ""
}
className="form-field__input--inline"
>
<option value="LBC">{__("LBRY Credits (LBC)")}</option>
<option value="USD">{__("US Dollars")}</option>
</FormField>
</span>
);
}
}
export default FormFieldPrice;

View file

@ -1,6 +1,6 @@
import React from "react";
import { Modal } from "component/modal";
import { FormField } from "component/form.js";
import FormField from "component/formField";
class ModalRemoveFile extends React.PureComponent {
constructor(props) {

View file

@ -1,5 +1,5 @@
import React from "react";
import { FormField } from "component/form";
import FormField from "component/formField";
class FormFieldPrice extends React.PureComponent {
constructor(props) {
@ -20,7 +20,6 @@ class FormFieldPrice extends React.PureComponent {
},
});
this.props.onChange(event.target.name, this.state.price);
console.log(this.state.price);
}
handleFeeCurrencyChange(event) {
@ -31,13 +30,12 @@ class FormFieldPrice extends React.PureComponent {
},
});
this.props.onChange(event.target.name, this.state.price);
console.log(this.state.price);
}
render() {
const {
defaultFeeValue,
defaultCurrencyValue,
defaultAmount,
defaultCurrency,
placeholder,
min,
step,
@ -52,14 +50,14 @@ class FormFieldPrice extends React.PureComponent {
placeholder={placeholder || null}
step={step}
onChange={event => this.handleFeeAmountChange(event)}
defaultValue={defaultFeeValue}
defaultValue={defaultAmount}
className="form-field__input--inline"
/>
<FormField
type="select"
name="currency"
onChange={event => this.handleFeeCurrencyChange(event)}
defaultValue={defaultCurrencyValue}
defaultValue={defaultCurrency}
className="form-field__input--inline"
>
<option value="LBC">{__("LBRY credits")}</option>

View file

@ -2,4 +2,4 @@ import React from "react";
import { connect } from "react-redux";
import PublishForm from "./view";
export default connect()(PublishForm);
export default connect(null, null)(PublishForm);

View file

@ -1,6 +1,6 @@
import React from "react";
import lbryuri from "lbryuri";
import { FormField, FormRow } from "component/form.js";
import { FormRow } from "component/form.js";
import { BusyMessage } from "component/common";
import Link from "component/link";
@ -127,7 +127,10 @@ class ChannelSection extends React.PureComponent {
<div className="card__title-primary">
<h4>{__("Channel Name")}</h4>
<div className="card__subtitle">
{__("This is the channel that broadcasts your content.")}
{__(
"This is a username or handle that your content can be found under."
)}
{" "}
{__("Ex. @Marvel, @TheBeatles, @BooksByJoe")}
</div>
</div>

View file

@ -1,9 +1,10 @@
import React from "react";
import lbry from "lbry";
import lbryuri from "lbryuri";
import { FormField, FormRow } from "component/form.js";
import FormField from "component/formField";
import { FormRow } from "component/form.js";
import Link from "component/link";
import FormFieldPrice from "component/priceForm";
import FormFieldPrice from "component/formFieldPrice";
import Modal from "component/modal";
import { BusyMessage } from "component/common";
import ChannelSection from "./internal/channelSection";
@ -125,7 +126,7 @@ class PublishForm extends React.PureComponent {
this.props.publish(publishArgs).then(success, failure);
};
if (this.state.isFee) {
if (this.state.isFee && parseFloat(this.state.feeAmount) > 0) {
lbry.wallet_unused_address().then(address => {
metadata.fee = {
currency: this.state.feeCurrency,
@ -307,15 +308,9 @@ class PublishForm extends React.PureComponent {
});
}
handleFeeAmtAndCurrChange(event) {
name = event.target.name;
let targetValue = { [name]: event.target.value };
if ([name] == "amount") {
this.setState({ feeAmount: targetValue.amount });
} else {
this.setState({ feeCurrency: targetValue.currency });
}
handleFeeChange(newValue) {
this.state.feeAmount = newValue.amount;
this.state.feeCurrency = newValue.currency;
}
handleFeePrefChange(feeEnabled) {
@ -667,22 +662,26 @@ class PublishForm extends React.PureComponent {
checked={this.state.isFee}
/>
<span className={!this.state.isFee ? "hidden" : ""}>
{/*min=0.01 caused weird interactions with step (e.g. down from 5 equals 4.91 rather than 4.9) */}
<FormFieldPrice
min="0.01"
placeholder="5.00"
min="0"
step="0.1"
onChange={event => this.handleFeeAmtAndCurrChange(event)}
defaultFeeValue="5.00"
defaultCurrencyValue="LBC"
defaultValue={{ amount: 5.0, currency: "LBC" }}
onChange={val => this.handleFeeChange(val)}
/>
</span>
{/*
&& this.state.feeCurrency.toUpperCase() != "LBC"
for some reason, react does not trigger a re-render on currency change (despite trigger a state change), so
the above logic cannot be added to the below check
*/}
{this.state.isFee
? <div className="form-field__helper">
{__(
"If you choose to price this content in dollars, the number of credits charged will be adjusted based on the value of LBRY credits at the time of purchase."
"All content fees are charged in LBC. For non-LBC payment methods, the number of credits charged will be adjusted based on the value of LBRY credits at the time of purchase."
)}
</div>
: ""}
: null}
<FormRow
label="License"
type="select"

View file

@ -1,6 +1,6 @@
import lbry from "../lbry.js";
import React from "react";
import { FormField } from "../component/form.js";
import FormField from "component/formField";
import Link from "../component/link";
const fs = require("fs");

View file

@ -1,11 +1,6 @@
import React from "react";
import lbry from "lbry.js";
import lbryuri from "lbryuri.js";
import Link from "component/link";
import { FormField } from "component/form.js";
import { FileTile } from "component/fileTile";
import rewards from "rewards.js";
import lbryio from "lbryio.js";
import { BusyMessage, Thumbnail } from "component/common.js";
import FileList from "component/fileList";
import SubHeader from "component/subHeader";

View file

@ -1,11 +1,6 @@
import React from "react";
import lbry from "lbry.js";
import lbryuri from "lbryuri.js";
import Link from "component/link";
import { FormField } from "component/form.js";
import FileTile from "component/fileTile";
import rewards from "rewards.js";
import lbryio from "lbryio.js";
import { BusyMessage, Thumbnail } from "component/common.js";
import FileList from "component/fileList";
import SubHeader from "component/subHeader";

View file

@ -1,9 +1,10 @@
import React from "react";
import { FormField, FormRow } from "component/form.js";
import FormField from "component/formField";
import { FormRow } from "component/form.js";
import SubHeader from "component/subHeader";
import lbry from "lbry.js";
import Link from "component/link";
import FormFieldPrice from "component/priceForm";
import FormFieldPrice from "component/formFieldPrice";
const { remote } = require("electron");
@ -56,22 +57,8 @@ class SettingsPage extends React.PureComponent {
this.setDaemonSetting("download_directory", event.target.value);
}
onKeyFeeChange(name, price) {
var oldSettings = this.props.daemonSettings.max_key_fee;
var newSettings = {
amount: oldSettings.amount,
currency: oldSettings.currency,
};
if (name == "amount") {
newSettings.amount = Number(price.feeAmount);
console.log(newSettings.amount, price.feeAmount);
} else {
newSettings.currency = price.feeCurrency;
console.log(newSettings.amount, price.feeAmount);
}
this.setDaemonSetting("max_key_fee", newSettings);
onKeyFeeChange(newValue) {
this.setDaemonSetting("max_key_fee", newValue);
}
onKeyFeeDisableChange(isDisabled) {
@ -171,16 +158,17 @@ class SettingsPage extends React.PureComponent {
: __("Limit to")
}
/>
{!daemonSettings.disable_max_key_fee
? <FormFieldPrice
{!daemonSettings.disable_max_key_fee &&
<FormFieldPrice
min="0"
placeholder="50.0"
step="1"
onChange={this.onKeyFeeChange.bind(this)}
defaultFeeValue={daemonSettings.max_key_fee.amount}
defaultCurrencyValue={daemonSettings.max_key_fee.currency}
/>
: ""}
defaultValue={
daemonSettings.max_key_fee
? daemonSettings.max_key_fee
: { currency: "USD", amount: 50 }
}
/>}
</div>
<div className="form-field__helper">
{__(

View file

@ -4,7 +4,7 @@ const _selectState = state => state.settings || {};
export const selectDaemonSettings = createSelector(
_selectState,
state => state.daemonSettings || {}
state => state.daemonSettings
);
export const selectClientSettings = createSelector(

107
yarn.lock
View file

@ -243,6 +243,22 @@ concat-stream@1.6.0:
readable-stream "^2.2.2"
typedarray "^0.0.6"
concordance@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/concordance/-/concordance-2.0.0.tgz#c3c5dbffa83c29537df202bded8fa1d6aa94e805"
dependencies:
esutils "^2.0.2"
fast-diff "^1.1.1"
function-name-support "^0.2.0"
js-string-escape "^1.0.1"
lodash.clonedeep "^4.5.0"
lodash.flattendeep "^4.4.0"
lodash.merge "^4.6.0"
md5-hex "^2.0.0"
moment "^2.18.1"
semver "^5.3.0"
well-known-symbols "^1.0.0"
configstore@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/configstore/-/configstore-2.1.0.tgz#737a3a7036e9886102aa6099e47bb33ab1aba1a1"
@ -405,12 +421,12 @@ electron-builder@^11.7.0:
uuid-1345 "^0.99.6"
yargs "^6.6.0"
electron-debug@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/electron-debug/-/electron-debug-1.2.0.tgz#22e51a73e1bf095d0bb51a6c3d97a203364c4222"
electron-debug@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/electron-debug/-/electron-debug-1.4.0.tgz#bec7005522220a9d0622153352e1bbff0f37af2e"
dependencies:
electron-is-dev "^0.1.0"
electron-localshortcut "^2.0.0"
electron-is-dev "^0.3.0"
electron-localshortcut "^3.0.0"
electron-download-tf@3.1.0:
version "3.1.0"
@ -443,16 +459,19 @@ electron-is-accelerator@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/electron-is-accelerator/-/electron-is-accelerator-0.1.2.tgz#509e510c26a56b55e17f863a4b04e111846ab27b"
electron-is-dev@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/electron-is-dev/-/electron-is-dev-0.1.2.tgz#8a1043e32b3a1da1c3f553dce28ce764246167e3"
electron-is-dev@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/electron-is-dev/-/electron-is-dev-0.3.0.tgz#14e6fda5c68e9e4ecbeff9ccf037cbd7c05c5afe"
electron-localshortcut@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/electron-localshortcut/-/electron-localshortcut-2.0.2.tgz#6a1adcd6514c957328ec7912f5ccb5e1c10706db"
electron-localshortcut@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/electron-localshortcut/-/electron-localshortcut-3.0.1.tgz#cefc1bdbe897defc2e6d9c62c657353cfaefcb91"
dependencies:
debug "^2.6.8"
electron-is-accelerator "^0.1.0"
insp "^0.1.0"
keyboardevent-from-electron-accelerator "^0.7.0"
keyboardevents-areequal "^0.2.1"
electron-macos-sign@~1.5.0:
version "1.5.0"
@ -464,9 +483,9 @@ electron-macos-sign@~1.5.0:
isbinaryfile "^3.0.2"
plist "^2.0.1"
electron@^1.4.15:
version "1.7.3"
resolved "https://registry.yarnpkg.com/electron/-/electron-1.7.3.tgz#839cab0a0598835a100dceede215988ba99c325b"
electron@^1.7.5:
version "1.7.5"
resolved "https://registry.yarnpkg.com/electron/-/electron-1.7.5.tgz#065a3102bf8b87102df50c50985fefe6c569045b"
dependencies:
"@types/node" "^7.0.18"
electron-download "^3.0.1"
@ -490,6 +509,10 @@ esprima@^3.1.1:
version "3.1.3"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
esutils@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
extend@~3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"
@ -507,6 +530,10 @@ extsprintf@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550"
fast-diff@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.1.1.tgz#0aea0e4e605b6a2189f0e936d4b7fbaf1b7cfd9b"
fd-slicer@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65"
@ -572,6 +599,10 @@ fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
function-name-support@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/function-name-support/-/function-name-support-0.2.0.tgz#55d3bfaa6eafd505a50f9bc81fdf57564a0bb071"
get-caller-file@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5"
@ -704,6 +735,12 @@ ini@^1.3.4, ini@~1.3.0:
version "1.3.4"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e"
insp@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/insp/-/insp-0.1.0.tgz#1a532c78664745dbb1d2c08e39f8d4832ab26349"
dependencies:
concordance "^2.0.0"
invert-kv@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
@ -780,6 +817,10 @@ isstream@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
js-string-escape@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef"
js-yaml@^3.7.0:
version "3.8.4"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.4.tgz#520b4564f86573ba96662af85a8cafa7b4b5a6f6"
@ -824,6 +865,14 @@ jsprim@^1.2.2:
json-schema "0.2.3"
verror "1.3.6"
keyboardevent-from-electron-accelerator@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/keyboardevent-from-electron-accelerator/-/keyboardevent-from-electron-accelerator-0.7.0.tgz#0532545ac08ce2dd1bacd81f2a8a9c67abee7a1a"
keyboardevents-areequal@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/keyboardevents-areequal/-/keyboardevents-areequal-0.2.1.tgz#4431d04fecc44f364cda73cf3f3270e177a54b8c"
klaw@^1.0.0:
version "1.3.1"
resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439"
@ -856,6 +905,18 @@ load-json-file@^1.0.0:
pinkie-promise "^2.0.0"
strip-bom "^2.0.0"
lodash.clonedeep@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
lodash.flattendeep@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2"
lodash.merge@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.0.tgz#69884ba144ac33fe699737a6086deffadd0f89c5"
loud-rejection@^1.0.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f"
@ -875,6 +936,16 @@ 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"
md5-hex@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/md5-hex/-/md5-hex-2.0.0.tgz#d0588e9f1c74954492ecd24ac0ac6ce997d92e33"
dependencies:
md5-o-matic "^0.1.1"
md5-o-matic@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/md5-o-matic/-/md5-o-matic-0.1.1.tgz#822bccd65e117c514fab176b25945d54100a03c3"
meow@^3.1.0:
version "3.7.0"
resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb"
@ -930,6 +1001,10 @@ mkdirp@^0.5.0, mkdirp@^0.5.1:
dependencies:
minimist "0.0.8"
moment@^2.18.1:
version "2.18.1"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.18.1.tgz#c36193dd3ce1c2eed2adb7c802dbbc77a81b1c0f"
ms@0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098"
@ -1502,6 +1577,10 @@ verror@1.3.6:
dependencies:
extsprintf "1.0.2"
well-known-symbols@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/well-known-symbols/-/well-known-symbols-1.0.0.tgz#73c78ae81a7726a8fa598e2880801c8b16225518"
which-module@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f"