Merge branch 'master' into restructuring
This commit is contained in:
commit
87d294a151
47 changed files with 1380 additions and 123 deletions
|
@ -8,12 +8,14 @@ Web UI version numbers should always match the corresponding version of LBRY App
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
### Added
|
### Added
|
||||||
*
|
* ShapeShift integration
|
||||||
*
|
*
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
* The credit balance displayed in the main app navigation displays two decimal places instead of one.
|
* The credit balance displayed in the main app navigation displays two decimal places instead of one.
|
||||||
* Moved all redux code into /redux folder
|
* Moved all redux code into /redux folder
|
||||||
|
* Channel names in pages are highlighted to indicate them being clickable(#814)
|
||||||
|
*
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
* Long channel names causing inconsistent thumbnail sizes (#721)
|
* Long channel names causing inconsistent thumbnail sizes (#721)
|
||||||
|
|
10
package.json
10
package.json
|
@ -57,7 +57,12 @@
|
||||||
"semver": "^5.3.0",
|
"semver": "^5.3.0",
|
||||||
"source-map-support": "^0.5.0",
|
"source-map-support": "^0.5.0",
|
||||||
"tree-kill": "^1.1.0",
|
"tree-kill": "^1.1.0",
|
||||||
"y18n": "^3.2.1"
|
"y18n": "^3.2.1",
|
||||||
|
"amplitude-js": "^4.0.0",
|
||||||
|
"classnames": "^2.2.5",
|
||||||
|
"formik": "^0.10.4",
|
||||||
|
"qrcode.react": "^0.7.2",
|
||||||
|
"shapeshift.io": "^1.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel-plugin-module-resolver": "^3.0.0",
|
"babel-plugin-module-resolver": "^3.0.0",
|
||||||
|
@ -80,7 +85,8 @@
|
||||||
"prettier": "^1.4.2",
|
"prettier": "^1.4.2",
|
||||||
"sass-loader": "^6.0.6",
|
"sass-loader": "^6.0.6",
|
||||||
"webpack": "^3.10.0",
|
"webpack": "^3.10.0",
|
||||||
"webpack-build-notifier": "^0.1.18"
|
"webpack-build-notifier": "^0.1.18",
|
||||||
|
"bluebird": "^3.5.1"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"webpack/webpack-sources": "1.0.1"
|
"webpack/webpack-sources": "1.0.1"
|
||||||
|
|
|
@ -12,6 +12,9 @@ flow-typed
|
||||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe
|
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe
|
||||||
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue
|
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue
|
||||||
module.name_mapper='^constants\(.*\)$' -> '<PROJECT_ROOT>/js/constants\1'
|
module.name_mapper='^constants\(.*\)$' -> '<PROJECT_ROOT>/js/constants\1'
|
||||||
|
module.name_mapper='^util\(.*\)$' -> '<PROJECT_ROOT>/js/util\1'
|
||||||
module.name_mapper='^redux\(.*\)$' -> '<PROJECT_ROOT>/js/redux\1'
|
module.name_mapper='^redux\(.*\)$' -> '<PROJECT_ROOT>/js/redux\1'
|
||||||
|
module.name_mapper='^types\(.*\)$' -> '<PROJECT_ROOT>/js/types\1'
|
||||||
|
module.name_mapper='^component\(.*\)$' -> '<PROJECT_ROOT>/js/component\1'
|
||||||
|
|
||||||
[strict]
|
[strict]
|
||||||
|
|
7
src/renderer/component/address/index.js
Normal file
7
src/renderer/component/address/index.js
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { doShowSnackBar } from "redux/actions/app";
|
||||||
|
import Address from "./view";
|
||||||
|
|
||||||
|
export default connect(null, {
|
||||||
|
doShowSnackBar,
|
||||||
|
})(Address);
|
52
src/renderer/component/address/view.jsx
Normal file
52
src/renderer/component/address/view.jsx
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import React from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import { clipboard } from "electron";
|
||||||
|
import Link from "component/link";
|
||||||
|
import classnames from "classnames";
|
||||||
|
|
||||||
|
export default class Address extends React.PureComponent {
|
||||||
|
static propTypes = {
|
||||||
|
address: PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this._inputElem = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { address, showCopyButton, doShowSnackBar } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="form-field form-field--address">
|
||||||
|
<input
|
||||||
|
className={classnames("input-copyable", {
|
||||||
|
"input-copyable--with-copy-btn": showCopyButton,
|
||||||
|
})}
|
||||||
|
type="text"
|
||||||
|
ref={input => {
|
||||||
|
this._inputElem = input;
|
||||||
|
}}
|
||||||
|
onFocus={() => {
|
||||||
|
this._inputElem.select();
|
||||||
|
}}
|
||||||
|
readOnly="readonly"
|
||||||
|
value={address || ""}
|
||||||
|
/>
|
||||||
|
{showCopyButton && (
|
||||||
|
<span className="header__item">
|
||||||
|
<Link
|
||||||
|
button="alt button--flat"
|
||||||
|
icon="clipboard"
|
||||||
|
onClick={() => {
|
||||||
|
clipboard.writeText(address);
|
||||||
|
doShowSnackBar({ message: __("Address copied") });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -137,42 +137,6 @@ export class CreditAmount extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let addressStyle = {
|
|
||||||
fontFamily:
|
|
||||||
'"Consolas", "Lucida Console", "Adobe Source Code Pro", monospace',
|
|
||||||
};
|
|
||||||
export class Address extends React.PureComponent {
|
|
||||||
static propTypes = {
|
|
||||||
address: PropTypes.string,
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this._inputElem = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div className="form-field form-field--address">
|
|
||||||
<input
|
|
||||||
className="input-copyable"
|
|
||||||
type="text"
|
|
||||||
ref={input => {
|
|
||||||
this._inputElem = input;
|
|
||||||
}}
|
|
||||||
onFocus={() => {
|
|
||||||
this._inputElem.select();
|
|
||||||
}}
|
|
||||||
style={addressStyle}
|
|
||||||
readOnly="readonly"
|
|
||||||
value={this.props.address || ""}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Thumbnail extends React.PureComponent {
|
export class Thumbnail extends React.PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
src: PropTypes.string,
|
src: PropTypes.string,
|
||||||
|
|
16
src/renderer/component/common/spinner.jsx
Normal file
16
src/renderer/component/common/spinner.jsx
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import React from "react";
|
||||||
|
import classnames from "classnames";
|
||||||
|
|
||||||
|
export default ({ dark, className }) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={classnames(
|
||||||
|
"spinner",
|
||||||
|
{
|
||||||
|
"spinner--dark": dark,
|
||||||
|
},
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
|
@ -14,11 +14,12 @@ const Link = props => {
|
||||||
navigate,
|
navigate,
|
||||||
navigateParams,
|
navigateParams,
|
||||||
doNavigate,
|
doNavigate,
|
||||||
|
className,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const className =
|
const combinedClassName =
|
||||||
(props.className || "") +
|
(className || "") +
|
||||||
(!props.className && !button ? "button-text" : "") + // Non-button links get the same look as text buttons
|
(!className && !button ? "button-text" : "") + // Non-button links get the same look as text buttons
|
||||||
(button ? " button-block button-" + button + " button-set-item" : "") +
|
(button ? " button-block button-" + button + " button-set-item" : "") +
|
||||||
(disabled ? " disabled" : "");
|
(disabled ? " disabled" : "");
|
||||||
|
|
||||||
|
@ -43,7 +44,7 @@ const Link = props => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
className={className}
|
className={combinedClassName}
|
||||||
href={href || "javascript:;"}
|
href={href || "javascript:;"}
|
||||||
title={title}
|
title={title}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
|
|
26
src/renderer/component/shapeShift/index.js
Normal file
26
src/renderer/component/shapeShift/index.js
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import {
|
||||||
|
createShapeShift,
|
||||||
|
shapeShiftInit,
|
||||||
|
getCoinStats,
|
||||||
|
clearShapeShift,
|
||||||
|
getActiveShift,
|
||||||
|
} from "redux/actions/shape_shift";
|
||||||
|
import { doShowSnackBar } from "redux/actions/app";
|
||||||
|
import { selectReceiveAddress } from "redux/selectors/wallet";
|
||||||
|
import { selectShapeShift } from "redux/selectors/shape_shift";
|
||||||
|
import ShapeShift from "./view";
|
||||||
|
|
||||||
|
const select = state => ({
|
||||||
|
receiveAddress: selectReceiveAddress(state),
|
||||||
|
shapeShift: selectShapeShift(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(select, {
|
||||||
|
shapeShiftInit,
|
||||||
|
getCoinStats,
|
||||||
|
createShapeShift,
|
||||||
|
clearShapeShift,
|
||||||
|
getActiveShift,
|
||||||
|
doShowSnackBar,
|
||||||
|
})(ShapeShift);
|
161
src/renderer/component/shapeShift/internal/active-shift.jsx
Normal file
161
src/renderer/component/shapeShift/internal/active-shift.jsx
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
// @flow
|
||||||
|
import * as React from "react";
|
||||||
|
import QRCode from "qrcode.react";
|
||||||
|
import * as statuses from "constants/shape_shift";
|
||||||
|
import Address from "component/address";
|
||||||
|
import Link from "component/link";
|
||||||
|
import type { Dispatch } from "redux/actions/shape_shift";
|
||||||
|
import ShiftMarketInfo from "./market_info";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
shiftState: ?string,
|
||||||
|
shiftCoinType: ?string,
|
||||||
|
shiftDepositAddress: ?string,
|
||||||
|
shiftReturnAddress: ?string,
|
||||||
|
shiftOrderId: ?string,
|
||||||
|
originCoinDepositMax: ?number,
|
||||||
|
clearShapeShift: Dispatch,
|
||||||
|
doShowSnackBar: Dispatch,
|
||||||
|
getActiveShift: Dispatch,
|
||||||
|
shapeShiftRate: ?number,
|
||||||
|
originCoinDepositMax: ?number,
|
||||||
|
originCoinDepositFee: ?number,
|
||||||
|
originCoinDepositMin: ?string,
|
||||||
|
};
|
||||||
|
|
||||||
|
class ActiveShapeShift extends React.PureComponent<Props> {
|
||||||
|
continousFetch: ?number;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.continousFetch = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const { getActiveShift, shiftDepositAddress } = this.props;
|
||||||
|
|
||||||
|
getActiveShift(shiftDepositAddress);
|
||||||
|
this.continousFetch = setInterval(() => {
|
||||||
|
getActiveShift(shiftDepositAddress);
|
||||||
|
}, 10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUpdate(nextProps: Props) {
|
||||||
|
const { shiftState } = nextProps;
|
||||||
|
if (shiftState === statuses.COMPLETE) {
|
||||||
|
this.clearContinuousFetch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.clearContinuousFetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
clearContinuousFetch() {
|
||||||
|
if (this.continousFetch) {
|
||||||
|
clearInterval(this.continousFetch);
|
||||||
|
this.continousFetch = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
shiftCoinType,
|
||||||
|
shiftDepositAddress,
|
||||||
|
shiftReturnAddress,
|
||||||
|
shiftOrderId,
|
||||||
|
shiftState,
|
||||||
|
originCoinDepositMax,
|
||||||
|
clearShapeShift,
|
||||||
|
doShowSnackBar,
|
||||||
|
shapeShiftRate,
|
||||||
|
originCoinDepositFee,
|
||||||
|
originCoinDepositMin,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{shiftState === statuses.NO_DEPOSITS && (
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
Send up to{" "}
|
||||||
|
<span className="credit-amount--bold">
|
||||||
|
{originCoinDepositMax} {shiftCoinType}
|
||||||
|
</span>{" "}
|
||||||
|
to the address below.
|
||||||
|
</p>
|
||||||
|
<ShiftMarketInfo
|
||||||
|
originCoin={shiftCoinType}
|
||||||
|
shapeShiftRate={shapeShiftRate}
|
||||||
|
originCoinDepositFee={originCoinDepositFee}
|
||||||
|
originCoinDepositMin={originCoinDepositMin}
|
||||||
|
originCoinDepositMax={originCoinDepositMax}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="shapeshift__deposit-address-wrapper">
|
||||||
|
<Address address={shiftDepositAddress} showCopyButton />
|
||||||
|
<div className="shapeshift__qrcode">
|
||||||
|
<QRCode value={shiftDepositAddress} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{shiftState === statuses.RECEIVED && (
|
||||||
|
<div className="card__content--extra-vertical-space">
|
||||||
|
<p>
|
||||||
|
{__(
|
||||||
|
"ShapeShift has received your payment! Sending the funds to your LBRY wallet."
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<span className="help">
|
||||||
|
{__("This can take a while, especially with BTC.")}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{shiftState === statuses.COMPLETE && (
|
||||||
|
<div className="card__content--extra-vertical-space">
|
||||||
|
<p>
|
||||||
|
{__(
|
||||||
|
"Transaction complete! You should see the new LBC in your wallet."
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="card__actions card__actions--only-vertical">
|
||||||
|
<Link
|
||||||
|
button={shiftState === statuses.COMPLETE ? "primary" : "alt"}
|
||||||
|
onClick={clearShapeShift}
|
||||||
|
label={
|
||||||
|
shiftState === statuses.COMPLETE ||
|
||||||
|
shiftState === statuses.RECEIVED
|
||||||
|
? __("Done")
|
||||||
|
: __("Cancel")
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{shiftOrderId && (
|
||||||
|
<span className="shapeshift__link">
|
||||||
|
<Link
|
||||||
|
button="text"
|
||||||
|
label={__("View the status on Shapeshift.io")}
|
||||||
|
href={`https://shapeshift.io/#/status/${shiftOrderId}`}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{shiftState === statuses.NO_DEPOSITS &&
|
||||||
|
shiftReturnAddress && (
|
||||||
|
<div className="shapeshift__actions-help">
|
||||||
|
<span className="help">
|
||||||
|
If the transaction doesn't go through, ShapeShift will return
|
||||||
|
your {shiftCoinType} back to {shiftReturnAddress}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ActiveShapeShift;
|
109
src/renderer/component/shapeShift/internal/form.jsx
Normal file
109
src/renderer/component/shapeShift/internal/form.jsx
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
import React from "react";
|
||||||
|
import Link from "component/link";
|
||||||
|
import { getExampleAddress } from "util/shape_shift";
|
||||||
|
import { Submit, FormRow } from "component/form";
|
||||||
|
import type { ShapeShiftFormValues, Dispatch } from "redux/actions/shape_shift";
|
||||||
|
import ShiftMarketInfo from "./market_info";
|
||||||
|
|
||||||
|
type ShapeShiftFormErrors = {
|
||||||
|
returnAddress?: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
values: ShapeShiftFormValues,
|
||||||
|
errors: ShapeShiftFormErrors,
|
||||||
|
touched: boolean,
|
||||||
|
handleChange: Event => any,
|
||||||
|
handleBlur: Event => any,
|
||||||
|
handleSubmit: Event => any,
|
||||||
|
isSubmitting: boolean,
|
||||||
|
shiftSupportedCoins: Array<string>,
|
||||||
|
originCoin: string,
|
||||||
|
updating: boolean,
|
||||||
|
getCoinStats: Dispatch,
|
||||||
|
receiveAddress: string,
|
||||||
|
originCoinDepositFee: number,
|
||||||
|
originCoinDepositMin: string,
|
||||||
|
originCoinDepositMax: number,
|
||||||
|
shapeShiftRate: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default (props: Props) => {
|
||||||
|
const {
|
||||||
|
values,
|
||||||
|
errors,
|
||||||
|
touched,
|
||||||
|
handleChange,
|
||||||
|
handleBlur,
|
||||||
|
handleSubmit,
|
||||||
|
isSubmitting,
|
||||||
|
shiftSupportedCoins,
|
||||||
|
originCoin,
|
||||||
|
updating,
|
||||||
|
getCoinStats,
|
||||||
|
receiveAddress,
|
||||||
|
originCoinDepositMax,
|
||||||
|
originCoinDepositMin,
|
||||||
|
originCoinDepositFee,
|
||||||
|
shapeShiftRate,
|
||||||
|
} = props;
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<div className="form-field">
|
||||||
|
<span>{__("Exchange")} </span>
|
||||||
|
<select
|
||||||
|
className="form-field__input form-field__input-select"
|
||||||
|
name="originCoin"
|
||||||
|
onChange={e => {
|
||||||
|
getCoinStats(e.target.value);
|
||||||
|
handleChange(e);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{shiftSupportedCoins.map(coin => (
|
||||||
|
<option key={coin} value={coin}>
|
||||||
|
{coin}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
<span> {__("for LBC")}</span>
|
||||||
|
<div className="shapeshift__tx-info">
|
||||||
|
{!updating &&
|
||||||
|
originCoinDepositMax && (
|
||||||
|
<ShiftMarketInfo
|
||||||
|
originCoin={originCoin}
|
||||||
|
shapeShiftRate={shapeShiftRate}
|
||||||
|
originCoinDepositFee={originCoinDepositFee}
|
||||||
|
originCoinDepositMin={originCoinDepositMin}
|
||||||
|
originCoinDepositMax={originCoinDepositMax}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<FormRow
|
||||||
|
type="text"
|
||||||
|
name="returnAddress"
|
||||||
|
placeholder={getExampleAddress(originCoin)}
|
||||||
|
label={__("Return address")}
|
||||||
|
onChange={handleChange}
|
||||||
|
onBlur={handleBlur}
|
||||||
|
value={values.returnAddress}
|
||||||
|
errorMessage={errors.returnAddress}
|
||||||
|
hasError={touched.returnAddress && !!errors.returnAddress}
|
||||||
|
/>
|
||||||
|
<span className="help">
|
||||||
|
<span>
|
||||||
|
({__("optional but recommended")}) {__("We will return your")}{" "}
|
||||||
|
{originCoin}{" "}
|
||||||
|
{__("to this address if the transaction doesn't go through.")}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<div className="card__actions card__actions--only-vertical">
|
||||||
|
<Submit
|
||||||
|
label={__("Begin Conversion")}
|
||||||
|
disabled={isSubmitting || !!Object.keys(errors).length}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
34
src/renderer/component/shapeShift/internal/market_info.jsx
Normal file
34
src/renderer/component/shapeShift/internal/market_info.jsx
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
// @flow
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
shapeShiftRate: ?number,
|
||||||
|
originCoin: ?string,
|
||||||
|
originCoinDepositFee: ?number,
|
||||||
|
originCoinDepositMax: ?number,
|
||||||
|
originCoinDepositMin: ?string,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default (props: Props) => {
|
||||||
|
const {
|
||||||
|
shapeShiftRate,
|
||||||
|
originCoin,
|
||||||
|
originCoinDepositFee,
|
||||||
|
originCoinDepositMax,
|
||||||
|
originCoinDepositMin,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<span className="help">
|
||||||
|
{__("Receive")} {shapeShiftRate} LBC
|
||||||
|
{" / "}
|
||||||
|
{"1"} {originCoin} {__("less")} {originCoinDepositFee} LBC {__("fee")}.
|
||||||
|
<br />
|
||||||
|
{__("Exchange max")}: {originCoinDepositMax} {originCoin}
|
||||||
|
<br />
|
||||||
|
{__("Exchange min")}: {originCoinDepositMin} {originCoin}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
150
src/renderer/component/shapeShift/view.jsx
Normal file
150
src/renderer/component/shapeShift/view.jsx
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
// @flow
|
||||||
|
import * as React from "react";
|
||||||
|
import { shell } from "electron";
|
||||||
|
import { Formik } from "formik";
|
||||||
|
import classnames from "classnames";
|
||||||
|
import * as statuses from "constants/shape_shift";
|
||||||
|
import { validateShapeShiftForm } from "util/shape_shift";
|
||||||
|
import Link from "component/link";
|
||||||
|
import Spinner from "component/common/spinner";
|
||||||
|
import { BusyMessage } from "component/common";
|
||||||
|
import ShapeShiftForm from "./internal/form";
|
||||||
|
import ActiveShapeShift from "./internal/active-shift";
|
||||||
|
|
||||||
|
import type { ShapeShiftState } from "redux/reducers/shape_shift";
|
||||||
|
import type { Dispatch, ShapeShiftFormValues } from "redux/actions/shape_shift";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
shapeShift: ShapeShiftState,
|
||||||
|
getCoinStats: Dispatch,
|
||||||
|
createShapeShift: Dispatch,
|
||||||
|
clearShapeShift: Dispatch,
|
||||||
|
getActiveShift: Dispatch,
|
||||||
|
doShowSnackBar: Dispatch,
|
||||||
|
shapeShiftInit: Dispatch,
|
||||||
|
receiveAddress: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
class ShapeShift extends React.PureComponent<Props> {
|
||||||
|
componentDidMount() {
|
||||||
|
const {
|
||||||
|
shapeShiftInit,
|
||||||
|
shapeShift: { hasActiveShift, shiftSupportedCoins },
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
if (!hasActiveShift && !shiftSupportedCoins.length) {
|
||||||
|
// calls shapeshift to see list of supported coins for shifting
|
||||||
|
shapeShiftInit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
getCoinStats,
|
||||||
|
receiveAddress,
|
||||||
|
createShapeShift,
|
||||||
|
shapeShift,
|
||||||
|
clearShapeShift,
|
||||||
|
getActiveShift,
|
||||||
|
doShowSnackBar,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const {
|
||||||
|
loading,
|
||||||
|
updating,
|
||||||
|
error,
|
||||||
|
shiftSupportedCoins,
|
||||||
|
hasActiveShift,
|
||||||
|
originCoin,
|
||||||
|
// ShapeShift response values
|
||||||
|
originCoinDepositMax,
|
||||||
|
originCoinDepositMin,
|
||||||
|
originCoinDepositFee,
|
||||||
|
shiftDepositAddress,
|
||||||
|
shiftReturnAddress,
|
||||||
|
shiftCoinType,
|
||||||
|
shiftOrderId,
|
||||||
|
shiftState,
|
||||||
|
shapeShiftRate,
|
||||||
|
} = shapeShift;
|
||||||
|
|
||||||
|
const initialFormValues: ShapeShiftFormValues = {
|
||||||
|
receiveAddress,
|
||||||
|
originCoin: "BTC",
|
||||||
|
returnAddress: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
// add the "shapeshift__intital-wrapper class so we can avoid content jumping once everything loads"
|
||||||
|
// it just gives the section a min-height equal to the height of the content when the form is rendered
|
||||||
|
// if the markup below changes for the initial render (form.jsx) there will be content jumping
|
||||||
|
// the styling in shapeshift.scss will need to be updated to the correct min-height
|
||||||
|
<section
|
||||||
|
className={classnames("card shapeshift__wrapper", {
|
||||||
|
"shapeshift__initial-wrapper": loading,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<div className="card__title-primary">
|
||||||
|
<h3>{__("Convert Crypto to LBC")}</h3>
|
||||||
|
<p className="help">
|
||||||
|
{__("Powered by ShapeShift. Read our FAQ")}{" "}
|
||||||
|
<Link href="https://lbry.io/faq/shapeshift">{__("here")}</Link>.
|
||||||
|
{hasActiveShift &&
|
||||||
|
shiftState !== "complete" && (
|
||||||
|
<span>{__("This will update automatically.")}</span>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="card__content shapeshift__content">
|
||||||
|
{error && <div className="form-field__error">{error}</div>}
|
||||||
|
{loading && <Spinner dark />}
|
||||||
|
{!loading &&
|
||||||
|
!hasActiveShift &&
|
||||||
|
!!shiftSupportedCoins.length && (
|
||||||
|
<Formik
|
||||||
|
onSubmit={createShapeShift}
|
||||||
|
validate={validateShapeShiftForm}
|
||||||
|
initialValues={initialFormValues}
|
||||||
|
render={formProps => (
|
||||||
|
<ShapeShiftForm
|
||||||
|
{...formProps}
|
||||||
|
updating={updating}
|
||||||
|
originCoin={originCoin}
|
||||||
|
shiftSupportedCoins={shiftSupportedCoins}
|
||||||
|
getCoinStats={getCoinStats}
|
||||||
|
receiveAddress={receiveAddress}
|
||||||
|
originCoinDepositMax={originCoinDepositMax}
|
||||||
|
originCoinDepositMin={originCoinDepositMin}
|
||||||
|
originCoinDepositFee={originCoinDepositFee}
|
||||||
|
shapeShiftRate={shapeShiftRate}
|
||||||
|
updating={updating}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{hasActiveShift && (
|
||||||
|
<ActiveShapeShift
|
||||||
|
getActiveShift={getActiveShift}
|
||||||
|
shiftCoinType={shiftCoinType}
|
||||||
|
shiftReturnAddress={shiftReturnAddress}
|
||||||
|
shiftDepositAddress={shiftDepositAddress}
|
||||||
|
originCoinDepositMax={originCoinDepositMax}
|
||||||
|
shiftOrderId={shiftOrderId}
|
||||||
|
shiftState={shiftState}
|
||||||
|
clearShapeShift={clearShapeShift}
|
||||||
|
doShowSnackBar={doShowSnackBar}
|
||||||
|
originCoinDepositMax={originCoinDepositMax}
|
||||||
|
originCoinDepositMin={originCoinDepositMin}
|
||||||
|
originCoinDepositFee={originCoinDepositFee}
|
||||||
|
shapeShiftRate={shapeShiftRate}
|
||||||
|
updating={updating}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ShapeShift;
|
|
@ -1,7 +1,8 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Icon } from "component/common";
|
import { Icon } from "component/common";
|
||||||
import Link from "component/link";
|
import Link from "component/link";
|
||||||
import lbryuri from "lbryuri.js";
|
import lbryuri from "lbryuri";
|
||||||
|
import classnames from "classnames";
|
||||||
|
|
||||||
class UriIndicator extends React.PureComponent {
|
class UriIndicator extends React.PureComponent {
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
|
@ -60,7 +61,12 @@ class UriIndicator extends React.PureComponent {
|
||||||
|
|
||||||
const inner = (
|
const inner = (
|
||||||
<span>
|
<span>
|
||||||
<span className={`channel-name ${smallCard && "channel-name--small"}`}>
|
<span
|
||||||
|
className={classnames("channel-name", {
|
||||||
|
"channel-name--small": smallCard,
|
||||||
|
"button-text no-underline": link,
|
||||||
|
})}
|
||||||
|
>
|
||||||
{channelName}
|
{channelName}
|
||||||
</span>{" "}
|
</span>{" "}
|
||||||
{!signatureIsValid ? (
|
{!signatureIsValid ? (
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import Spinner from "component/common/spinner";
|
||||||
|
|
||||||
const LoadingScreen = ({ status, spinner = true }) => (
|
const LoadingScreen = ({ status, spinner = true }) => (
|
||||||
<div className="video__loading-screen">
|
<div className="video__loading-screen">
|
||||||
<div>
|
<div>
|
||||||
{spinner && <div className="video__loading-spinner" />}
|
{spinner && <Spinner />}
|
||||||
|
|
||||||
<div className="video__loading-status">{status}</div>
|
<div className="video__loading-status">{status}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import Link from "component/link";
|
import Link from "component/link";
|
||||||
import { Address } from "component/common";
|
import Address from "component/address";
|
||||||
|
|
||||||
class WalletAddress extends React.PureComponent {
|
class WalletAddress extends React.PureComponent {
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
|
|
|
@ -144,3 +144,18 @@ export const FETCH_REWARD_CONTENT_COMPLETED = "FETCH_REWARD_CONTENT_COMPLETED";
|
||||||
//Language
|
//Language
|
||||||
export const DOWNLOAD_LANGUAGE_SUCCEEDED = "DOWNLOAD_LANGUAGE_SUCCEEDED";
|
export const DOWNLOAD_LANGUAGE_SUCCEEDED = "DOWNLOAD_LANGUAGE_SUCCEEDED";
|
||||||
export const DOWNLOAD_LANGUAGE_FAILED = "DOWNLOAD_LANGUAGE_FAILED";
|
export const DOWNLOAD_LANGUAGE_FAILED = "DOWNLOAD_LANGUAGE_FAILED";
|
||||||
|
|
||||||
|
// ShapeShift
|
||||||
|
export const GET_SUPPORTED_COINS_START = "GET_SUPPORTED_COINS_START";
|
||||||
|
export const GET_SUPPORTED_COINS_SUCCESS = "GET_SUPPORTED_COINS_SUCCESS";
|
||||||
|
export const GET_SUPPORTED_COINS_FAIL = "GET_SUPPORTED_COINS_FAIL";
|
||||||
|
export const GET_COIN_STATS_START = "GET_COIN_STATS_START";
|
||||||
|
export const GET_COIN_STATS_SUCCESS = "GET_COIN_STATS_SUCCESS";
|
||||||
|
export const GET_COIN_STATS_FAIL = "GET_COIN_STATS_FAIL";
|
||||||
|
export const PREPARE_SHAPE_SHIFT_START = "PREPARE_SHAPE_SHIFT_START";
|
||||||
|
export const PREPARE_SHAPE_SHIFT_SUCCESS = "PREPARE_SHAPE_SHIFT_SUCCESS";
|
||||||
|
export const PREPARE_SHAPE_SHIFT_FAIL = "PREPARE_SHAPE_SHIFT_FAIL";
|
||||||
|
export const GET_ACTIVE_SHIFT_START = "GET_ACTIVE_SHIFT_START";
|
||||||
|
export const GET_ACTIVE_SHIFT_SUCCESS = "GET_ACTIVE_SHIFT_SUCCESS";
|
||||||
|
export const GET_ACTIVE_SHIFT_FAIL = "GET_ACTIVE_SHIFT_FAIL";
|
||||||
|
export const CLEAR_SHAPE_SHIFT = "CLEAR_SHAPE_SHIFT";
|
||||||
|
|
3
src/renderer/constants/shape_shift.js
Normal file
3
src/renderer/constants/shape_shift.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export const NO_DEPOSITS = "no_deposits";
|
||||||
|
export const RECEIVED = "received";
|
||||||
|
export const COMPLETE = "complete";
|
3
src/renderer/flow-typed/bluebird.js
vendored
Normal file
3
src/renderer/flow-typed/bluebird.js
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
declare module 'bluebird' {
|
||||||
|
declare module.exports: any;
|
||||||
|
}
|
3
src/renderer/flow-typed/classnames.js
vendored
Normal file
3
src/renderer/flow-typed/classnames.js
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
declare module 'classnames' {
|
||||||
|
declare module.exports: any;
|
||||||
|
}
|
3
src/renderer/flow-typed/formik.js
vendored
Normal file
3
src/renderer/flow-typed/formik.js
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
declare module 'formik' {
|
||||||
|
declare module.exports: any;
|
||||||
|
}
|
1
src/renderer/flow-typed/i18n.js
vendored
Normal file
1
src/renderer/flow-typed/i18n.js
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
declare function __(a: string): string;
|
3
src/renderer/flow-typed/qrcode.react.js
vendored
Normal file
3
src/renderer/flow-typed/qrcode.react.js
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
declare module 'qrcode.react' {
|
||||||
|
declare module.exports: any;
|
||||||
|
}
|
3
src/renderer/flow-typed/shapeshift.io.js
vendored
Normal file
3
src/renderer/flow-typed/shapeshift.io.js
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
declare module 'shapeshift.io' {
|
||||||
|
declare module.exports: any;
|
||||||
|
}
|
|
@ -9,6 +9,8 @@ import { doDaemonReady } from "redux/actions/app";
|
||||||
import { doNavigate } from "redux/actions/navigation";
|
import { doNavigate } from "redux/actions/navigation";
|
||||||
import { doDownloadLanguages } from "redux/actions/settings";
|
import { doDownloadLanguages } from "redux/actions/settings";
|
||||||
import * as types from "constants/action_types";
|
import * as types from "constants/action_types";
|
||||||
|
import amplitude from "amplitude-js";
|
||||||
|
import lbry from "lbry";
|
||||||
import "scss/all.scss";
|
import "scss/all.scss";
|
||||||
|
|
||||||
const env = process.env.NODE_ENV || "production";
|
const env = process.env.NODE_ENV || "production";
|
||||||
|
@ -56,6 +58,23 @@ ipcRenderer.on("window-is-focused", (event, data) => {
|
||||||
document.addEventListener("click", event => {
|
document.addEventListener("click", event => {
|
||||||
var target = event.target;
|
var target = event.target;
|
||||||
while (target && target !== document) {
|
while (target && target !== document) {
|
||||||
|
if (target.matches("a") || target.matches("button")) {
|
||||||
|
// TODO: Look into using accessiblity labels (this would also make the app more accessible)
|
||||||
|
let hrefParts = window.location.href.split("#");
|
||||||
|
let element = target.title || target.text.trim();
|
||||||
|
if (element) {
|
||||||
|
amplitude.getInstance().logEvent("CLICK", {
|
||||||
|
target: element,
|
||||||
|
location:
|
||||||
|
hrefParts.length > 1 ? hrefParts[hrefParts.length - 1] : "/",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
amplitude.getInstance().logEvent("UNMARKED_CLICK", {
|
||||||
|
location:
|
||||||
|
hrefParts.length > 1 ? hrefParts[hrefParts.length - 1] : "/",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
target.matches('a[href^="http"]') ||
|
target.matches('a[href^="http"]') ||
|
||||||
target.matches('a[href^="mailto"]')
|
target.matches('a[href^="mailto"]')
|
||||||
|
@ -74,18 +93,28 @@ var init = function() {
|
||||||
app.store.dispatch(doDownloadLanguages());
|
app.store.dispatch(doDownloadLanguages());
|
||||||
|
|
||||||
function onDaemonReady() {
|
function onDaemonReady() {
|
||||||
window.sessionStorage.setItem("loaded", "y"); //once we've made it here once per session, we don't need to show splash again
|
lbry.status().then(info => {
|
||||||
app.store.dispatch(doDaemonReady());
|
amplitude.getInstance().init(
|
||||||
|
// Amplitude API Key
|
||||||
|
"0b130efdcbdbf86ec2f7f9eff354033e",
|
||||||
|
info.lbry_id,
|
||||||
|
null,
|
||||||
|
function() {
|
||||||
|
window.sessionStorage.setItem("loaded", "y"); //once we've made it here once per session, we don't need to show splash again
|
||||||
|
app.store.dispatch(doDaemonReady());
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<div>
|
<div>
|
||||||
<App />
|
<App />
|
||||||
<SnackBar />
|
<SnackBar />
|
||||||
</div>
|
</div>
|
||||||
</Provider>,
|
</Provider>,
|
||||||
document.getElementById('app')
|
document.getElementById('app')
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window.sessionStorage.getItem("loaded") == "y") {
|
if (window.sessionStorage.getItem("loaded") == "y") {
|
||||||
|
|
|
@ -2,15 +2,17 @@ import React from "react";
|
||||||
import SubHeader from "component/subHeader";
|
import SubHeader from "component/subHeader";
|
||||||
import Link from "component/link";
|
import Link from "component/link";
|
||||||
import WalletAddress from "component/walletAddress";
|
import WalletAddress from "component/walletAddress";
|
||||||
|
import ShapeShift from "component/shapeShift";
|
||||||
|
|
||||||
const ReceiveCreditsPage = props => {
|
const ReceiveCreditsPage = props => {
|
||||||
return (
|
return (
|
||||||
<main className="main--single-column">
|
<main className="main--single-column">
|
||||||
<SubHeader />
|
<SubHeader />
|
||||||
<WalletAddress />
|
<WalletAddress />
|
||||||
|
<ShapeShift />
|
||||||
<section className="card">
|
<section className="card">
|
||||||
<div className="card__title-primary">
|
<div className="card__title-primary">
|
||||||
<h3>{__("Where To Find Credits")}</h3>
|
<h3>{__("More ways to get LBRY Credits")}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="card__content">
|
<div className="card__content">
|
||||||
<p>
|
<p>
|
||||||
|
|
137
src/renderer/redux/actions/shape_shift.js
Normal file
137
src/renderer/redux/actions/shape_shift.js
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
// @flow
|
||||||
|
import Promise from "bluebird";
|
||||||
|
import * as types from "constants/action_types";
|
||||||
|
import { coinRegexPatterns } from "util/shape_shift";
|
||||||
|
import type {
|
||||||
|
GetSupportedCoinsSuccess,
|
||||||
|
GetCoinStatsStart,
|
||||||
|
GetCoinStatsSuccess,
|
||||||
|
GetCoinStatsFail,
|
||||||
|
PrepareShapeShiftSuccess,
|
||||||
|
PrepareShapeShiftFail,
|
||||||
|
GetActiveShiftSuccess,
|
||||||
|
GetActiveShiftFail,
|
||||||
|
} from "redux/reducers/shape_shift";
|
||||||
|
import type { FormikActions } from "types/common";
|
||||||
|
|
||||||
|
// use promise chains instead of callbacks for shapeshift api
|
||||||
|
const shapeShift = Promise.promisifyAll(require("shapeshift.io"));
|
||||||
|
|
||||||
|
// All ShapeShift actions
|
||||||
|
// Action types defined in the reducer will contain some payload
|
||||||
|
export type Action =
|
||||||
|
| { type: types.GET_SUPPORTED_COINS_START }
|
||||||
|
| { type: types.GET_SUPPORTED_COINS_FAIL }
|
||||||
|
| GetSupportedCoinsSuccess
|
||||||
|
| GetCoinStatsStart
|
||||||
|
| { type: types.GET_COIN_STATS_START }
|
||||||
|
| GetCoinStatsFail
|
||||||
|
| GetCoinStatsSuccess
|
||||||
|
| { type: types.PREPARE_SHAPE_SHIFT_START }
|
||||||
|
| PrepareShapeShiftFail
|
||||||
|
| PrepareShapeShiftSuccess
|
||||||
|
| { type: types.GET_ACTIVE_SHIFT_START }
|
||||||
|
| GetActiveShiftFail
|
||||||
|
| GetActiveShiftSuccess;
|
||||||
|
|
||||||
|
// Basic thunk types
|
||||||
|
// It would be nice to import these from types/common
|
||||||
|
// Not sure how that would work since they rely on the Action type
|
||||||
|
type PromiseAction = Promise<Action>;
|
||||||
|
type ThunkAction = (dispatch: Dispatch) => any;
|
||||||
|
export type Dispatch = (
|
||||||
|
action: Action | ThunkAction | PromiseAction | Array<Action>
|
||||||
|
) => any;
|
||||||
|
|
||||||
|
// ShapeShift form values
|
||||||
|
export type ShapeShiftFormValues = {
|
||||||
|
originCoin: string,
|
||||||
|
returnAddress: ?string,
|
||||||
|
receiveAddress: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const shapeShiftInit = () => (dispatch: Dispatch): ThunkAction => {
|
||||||
|
dispatch({ type: types.GET_SUPPORTED_COINS_START });
|
||||||
|
|
||||||
|
return shapeShift
|
||||||
|
.coinsAsync()
|
||||||
|
.then(coinData => {
|
||||||
|
let supportedCoins = [];
|
||||||
|
Object.keys(coinData).forEach(symbol => {
|
||||||
|
if (coinData[symbol].status === "available") {
|
||||||
|
supportedCoins.push(coinData[symbol]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// only use larger coins with client side validation
|
||||||
|
supportedCoins = supportedCoins
|
||||||
|
.filter(coin => coinRegexPatterns[coin.symbol])
|
||||||
|
.map(coin => coin.symbol);
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: types.GET_SUPPORTED_COINS_SUCCESS,
|
||||||
|
data: supportedCoins,
|
||||||
|
});
|
||||||
|
dispatch(getCoinStats(supportedCoins[0]));
|
||||||
|
})
|
||||||
|
.catch(err =>
|
||||||
|
dispatch({ type: types.GET_SUPPORTED_COINS_FAIL, data: err })
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCoinStats = (coin: string) => (
|
||||||
|
dispatch: Dispatch
|
||||||
|
): ThunkAction => {
|
||||||
|
const pair = `${coin.toLowerCase()}_lbc`;
|
||||||
|
|
||||||
|
dispatch({ type: types.GET_COIN_STATS_START, data: coin });
|
||||||
|
|
||||||
|
return shapeShift
|
||||||
|
.marketInfoAsync(pair)
|
||||||
|
.then(marketInfo =>
|
||||||
|
dispatch({ type: types.GET_COIN_STATS_SUCCESS, data: marketInfo })
|
||||||
|
)
|
||||||
|
.catch(err => dispatch({ type: types.GET_COIN_STATS_FAIL, data: err }));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createShapeShift = (
|
||||||
|
values: ShapeShiftFormValues,
|
||||||
|
actions: FormikActions
|
||||||
|
) => (dispatch: Dispatch): ThunkAction => {
|
||||||
|
const {
|
||||||
|
originCoin,
|
||||||
|
returnAddress,
|
||||||
|
receiveAddress: withdrawalAddress,
|
||||||
|
} = values;
|
||||||
|
|
||||||
|
const pair = `${originCoin.toLowerCase()}_lbc`;
|
||||||
|
const options = {
|
||||||
|
returnAddress: returnAddress,
|
||||||
|
};
|
||||||
|
|
||||||
|
dispatch({ type: types.PREPARE_SHAPE_SHIFT_START });
|
||||||
|
return shapeShift
|
||||||
|
.shiftAsync(withdrawalAddress, pair, options)
|
||||||
|
.then(res =>
|
||||||
|
dispatch({ type: types.PREPARE_SHAPE_SHIFT_SUCCESS, data: res })
|
||||||
|
)
|
||||||
|
.catch(err => {
|
||||||
|
dispatch({ type: types.PREPARE_SHAPE_SHIFT_FAIL, data: err });
|
||||||
|
// for formik to stop the submit
|
||||||
|
actions.setSubmitting(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getActiveShift = (depositAddress: string) => (
|
||||||
|
dispatch: Dispatch
|
||||||
|
): ThunkAction => {
|
||||||
|
dispatch({ type: types.GET_ACTIVE_SHIFT_START });
|
||||||
|
|
||||||
|
return shapeShift
|
||||||
|
.statusAsync(depositAddress)
|
||||||
|
.then(res => dispatch({ type: types.GET_ACTIVE_SHIFT_SUCCESS, data: res }))
|
||||||
|
.catch(err => dispatch({ type: types.GET_ACTIVE_SHIFT_FAIL, data: err }));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const clearShapeShift = () => (dispatch: Dispatch): Action =>
|
||||||
|
dispatch({ type: types.CLEAR_SHAPE_SHIFT });
|
|
@ -1,5 +1,6 @@
|
||||||
import * as types from "constants/action_types";
|
import * as types from "constants/action_types";
|
||||||
import { parseQueryParams } from "util/query_params";
|
import { parseQueryParams } from "util/query_params";
|
||||||
|
import amplitude from "amplitude-js";
|
||||||
|
|
||||||
const currentPath = () => {
|
const currentPath = () => {
|
||||||
const hash = document.location.hash;
|
const hash = document.location.hash;
|
||||||
|
@ -69,6 +70,14 @@ reducers[types.WINDOW_SCROLLED] = (state, action) => {
|
||||||
|
|
||||||
export default function reducer(state = defaultState, action) {
|
export default function reducer(state = defaultState, action) {
|
||||||
const handler = reducers[action.type];
|
const handler = reducers[action.type];
|
||||||
if (handler) return handler(state, action);
|
if (handler) {
|
||||||
|
let nextState = handler(state, action);
|
||||||
|
if (nextState.currentPath !== state.currentPath) {
|
||||||
|
amplitude
|
||||||
|
.getInstance()
|
||||||
|
.logEvent("NAVIGATION", { destination: nextState.currentPath });
|
||||||
|
}
|
||||||
|
return nextState;
|
||||||
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
245
src/renderer/redux/reducers/shape_shift.js
Normal file
245
src/renderer/redux/reducers/shape_shift.js
Normal file
|
@ -0,0 +1,245 @@
|
||||||
|
// @flow
|
||||||
|
import { handleActions } from "util/redux-utils";
|
||||||
|
import * as actions from "constants/action_types";
|
||||||
|
import * as statuses from "constants/shape_shift";
|
||||||
|
|
||||||
|
export type ShapeShiftState = {
|
||||||
|
loading: boolean,
|
||||||
|
updating: boolean,
|
||||||
|
shiftSupportedCoins: Array<string>,
|
||||||
|
hasActiveShift: boolean,
|
||||||
|
originCoin: ?string,
|
||||||
|
error: ?string,
|
||||||
|
shiftDepositAddress: ?string,
|
||||||
|
shiftReturnAddress: ?string,
|
||||||
|
shiftCoinType: ?string,
|
||||||
|
shiftOrderId: ?string,
|
||||||
|
shiftState: ?string,
|
||||||
|
originCoinDepositMax: ?number,
|
||||||
|
// originCoinDepositMin is a string because we need to convert it from scientifc notation
|
||||||
|
// it will usually be something like 0.00000001 coins
|
||||||
|
// using Number(x) or parseInt(x) will either change it back to scientific notation or round to zero
|
||||||
|
originCoinDepositMin: ?string,
|
||||||
|
originCoinDepositFee: ?number,
|
||||||
|
shapeShiftRate: ?number,
|
||||||
|
};
|
||||||
|
|
||||||
|
// All ShapeShift actions that will have some payload
|
||||||
|
export type GetSupportedCoinsSuccess = {
|
||||||
|
type: actions.GET_SUPPORTED_COINS_SUCCESS,
|
||||||
|
data: Array<string>,
|
||||||
|
};
|
||||||
|
export type GetCoinStatsStart = {
|
||||||
|
type: actions.GET_SUPPORTED_COINS_SUCCESS,
|
||||||
|
data: string,
|
||||||
|
};
|
||||||
|
export type GetCoinStatsSuccess = {
|
||||||
|
type: actions.GET_COIN_STATS_SUCCESS,
|
||||||
|
data: ShapeShiftMarketInfo,
|
||||||
|
};
|
||||||
|
export type GetCoinStatsFail = {
|
||||||
|
type: actions.GET_COIN_STATS_FAIL,
|
||||||
|
data: string,
|
||||||
|
};
|
||||||
|
export type PrepareShapeShiftSuccess = {
|
||||||
|
type: actions.PREPARE_SHAPE_SHIFT_SUCCESS,
|
||||||
|
data: ActiveShiftInfo,
|
||||||
|
};
|
||||||
|
export type PrepareShapeShiftFail = {
|
||||||
|
type: actions.PREPARE_SHAPE_SHIFT_FAIL,
|
||||||
|
data: ShapeShiftErrorResponse,
|
||||||
|
};
|
||||||
|
export type GetActiveShiftSuccess = {
|
||||||
|
type: actions.GET_ACTIVE_SHIFT_SUCCESS,
|
||||||
|
data: string,
|
||||||
|
};
|
||||||
|
export type GetActiveShiftFail = {
|
||||||
|
type: actions.GET_ACTIVE_SHIFT_FAIL,
|
||||||
|
data: ShapeShiftErrorResponse,
|
||||||
|
};
|
||||||
|
|
||||||
|
// ShapeShift sub-types
|
||||||
|
// Defined for actions that contain an object in the payload
|
||||||
|
type ShapeShiftMarketInfo = {
|
||||||
|
limit: number,
|
||||||
|
minimum: number,
|
||||||
|
minerFee: number,
|
||||||
|
rate: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
type ActiveShiftInfo = {
|
||||||
|
deposit: string,
|
||||||
|
depositType: string,
|
||||||
|
returnAddress: string,
|
||||||
|
orderId: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
type ShapeShiftErrorResponse = {
|
||||||
|
message: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultState: ShapeShiftState = {
|
||||||
|
loading: true,
|
||||||
|
updating: false,
|
||||||
|
shiftSupportedCoins: [],
|
||||||
|
hasActiveShift: false,
|
||||||
|
originCoin: undefined,
|
||||||
|
error: undefined,
|
||||||
|
shiftDepositAddress: undefined, // shapeshift address to send your coins to
|
||||||
|
shiftReturnAddress: undefined,
|
||||||
|
shiftCoinType: undefined,
|
||||||
|
shiftOrderId: undefined,
|
||||||
|
shiftState: undefined,
|
||||||
|
originCoinDepositMax: undefined,
|
||||||
|
originCoinDepositMin: undefined,
|
||||||
|
originCoinDepositFee: undefined,
|
||||||
|
shapeShiftRate: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default handleActions(
|
||||||
|
{
|
||||||
|
[actions.GET_SUPPORTED_COINS_START]: (
|
||||||
|
state: ShapeShiftState
|
||||||
|
): ShapeShiftState => ({
|
||||||
|
...state,
|
||||||
|
loading: true,
|
||||||
|
error: undefined,
|
||||||
|
}),
|
||||||
|
[actions.GET_SUPPORTED_COINS_SUCCESS]: (
|
||||||
|
state: ShapeShiftState,
|
||||||
|
action: GetSupportedCoinsSuccess
|
||||||
|
): ShapeShiftState => {
|
||||||
|
const shiftSupportedCoins = action.data;
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
error: undefined,
|
||||||
|
shiftSupportedCoins,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[actions.GET_SUPPORTED_COINS_FAIL]: (
|
||||||
|
state: ShapeShiftState
|
||||||
|
): ShapeShiftState => ({
|
||||||
|
...state,
|
||||||
|
loading: false,
|
||||||
|
error: "Error getting available coins",
|
||||||
|
}),
|
||||||
|
|
||||||
|
[actions.GET_COIN_STATS_START]: (
|
||||||
|
state: ShapeShiftState,
|
||||||
|
action: GetCoinStatsStart
|
||||||
|
): ShapeShiftState => {
|
||||||
|
const coin = action.data;
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
updating: true,
|
||||||
|
originCoin: coin,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[actions.GET_COIN_STATS_SUCCESS]: (
|
||||||
|
state: ShapeShiftState,
|
||||||
|
action: GetCoinStatsSuccess
|
||||||
|
): ShapeShiftState => {
|
||||||
|
const marketInfo: ShapeShiftMarketInfo = action.data;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loading: false,
|
||||||
|
updating: false,
|
||||||
|
originCoinDepositMax: marketInfo.limit,
|
||||||
|
// this will come in scientific notation
|
||||||
|
// toFixed shows the real number, then regex to remove trailing zeros
|
||||||
|
originCoinDepositMin: marketInfo.minimum
|
||||||
|
.toFixed(10)
|
||||||
|
.replace(/\.?0+$/, ""),
|
||||||
|
originCoinDepositFee: marketInfo.minerFee,
|
||||||
|
shapeShiftRate: marketInfo.rate,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[actions.GET_COIN_STATS_FAIL]: (
|
||||||
|
state: ShapeShiftState,
|
||||||
|
action: GetCoinStatsFail
|
||||||
|
): ShapeShiftState => {
|
||||||
|
const error = action.data;
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loading: false,
|
||||||
|
error,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
[actions.PREPARE_SHAPE_SHIFT_START]: (
|
||||||
|
state: ShapeShiftState
|
||||||
|
): ShapeShiftState => ({
|
||||||
|
...state,
|
||||||
|
error: undefined,
|
||||||
|
}),
|
||||||
|
[actions.PREPARE_SHAPE_SHIFT_SUCCESS]: (
|
||||||
|
state: ShapeShiftState,
|
||||||
|
action: PrepareShapeShiftSuccess
|
||||||
|
) => {
|
||||||
|
const activeShiftInfo: ActiveShiftInfo = action.data;
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
hasActiveShift: true,
|
||||||
|
shiftDepositAddress: activeShiftInfo.deposit,
|
||||||
|
shiftCoinType: activeShiftInfo.depositType,
|
||||||
|
shiftReturnAddress: activeShiftInfo.returnAddress,
|
||||||
|
shiftOrderId: activeShiftInfo.orderId,
|
||||||
|
shiftState: statuses.NO_DEPOSITS,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[actions.PREPARE_SHAPE_SHIFT_FAIL]: (
|
||||||
|
state: ShapeShiftState,
|
||||||
|
action: PrepareShapeShiftFail
|
||||||
|
) => {
|
||||||
|
const error: ShapeShiftErrorResponse = action.data;
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
error: error.message,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
[actions.CLEAR_SHAPE_SHIFT]: (state: ShapeShiftState): ShapeShiftState => ({
|
||||||
|
...state,
|
||||||
|
loading: false,
|
||||||
|
updating: false,
|
||||||
|
hasActiveShift: false,
|
||||||
|
shiftDepositAddress: undefined,
|
||||||
|
shiftReturnAddress: undefined,
|
||||||
|
shiftCoinType: undefined,
|
||||||
|
shiftOrderId: undefined,
|
||||||
|
originCoin: state.shiftSupportedCoins[0],
|
||||||
|
}),
|
||||||
|
|
||||||
|
[actions.GET_ACTIVE_SHIFT_START]: (
|
||||||
|
state: ShapeShiftState
|
||||||
|
): ShapeShiftState => ({
|
||||||
|
...state,
|
||||||
|
error: undefined,
|
||||||
|
updating: true,
|
||||||
|
}),
|
||||||
|
[actions.GET_ACTIVE_SHIFT_SUCCESS]: (
|
||||||
|
state: ShapeShiftState,
|
||||||
|
action: GetActiveShiftSuccess
|
||||||
|
): ShapeShiftState => {
|
||||||
|
const status = action.data;
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
updating: false,
|
||||||
|
shiftState: status,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[actions.GET_ACTIVE_SHIFT_FAIL]: (
|
||||||
|
state: ShapeShiftState,
|
||||||
|
action: GetActiveShiftFail
|
||||||
|
): ShapeShiftState => {
|
||||||
|
const error: ShapeShiftErrorResponse = action.data;
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
updating: false,
|
||||||
|
error: error.message,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultState
|
||||||
|
);
|
|
@ -44,8 +44,8 @@ export const selectHeaderLinks = createSelector(selectCurrentPage, page => {
|
||||||
return {
|
return {
|
||||||
wallet: __("Overview"),
|
wallet: __("Overview"),
|
||||||
history: __("History"),
|
history: __("History"),
|
||||||
send: __("Send"),
|
send: __("Send Credits"),
|
||||||
receive: __("Receive"),
|
receive: __("Get Credits"),
|
||||||
rewards: __("Rewards"),
|
rewards: __("Rewards"),
|
||||||
invite: __("Invites"),
|
invite: __("Invites"),
|
||||||
};
|
};
|
||||||
|
@ -78,9 +78,9 @@ export const selectPageTitle = createSelector(
|
||||||
case "wallet":
|
case "wallet":
|
||||||
return __("Wallet");
|
return __("Wallet");
|
||||||
case "send":
|
case "send":
|
||||||
return __("Send Credits");
|
return __("Send LBRY Credits");
|
||||||
case "receive":
|
case "receive":
|
||||||
return __("Wallet Address");
|
return __("Get LBRY Credits");
|
||||||
case "backup":
|
case "backup":
|
||||||
return __("Backup Your Wallet");
|
return __("Backup Your Wallet");
|
||||||
case "rewards":
|
case "rewards":
|
||||||
|
|
|
@ -65,7 +65,7 @@ export const selectWunderBarIcon = createSelector(
|
||||||
return "icon-envelope-open";
|
return "icon-envelope-open";
|
||||||
case "address":
|
case "address":
|
||||||
case "receive":
|
case "receive":
|
||||||
return "icon-address-book";
|
return "icon-credit-card";
|
||||||
case "wallet":
|
case "wallet":
|
||||||
case "backup":
|
case "backup":
|
||||||
return "icon-bank";
|
return "icon-bank";
|
||||||
|
|
7
src/renderer/redux/selectors/shape_shift.js
Normal file
7
src/renderer/redux/selectors/shape_shift.js
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { createSelector } from "reselect";
|
||||||
|
|
||||||
|
const _selectState = state => state.shapeShift;
|
||||||
|
|
||||||
|
export const selectShapeShift = createSelector(_selectState, state => ({
|
||||||
|
...state,
|
||||||
|
}));
|
|
@ -36,6 +36,11 @@ body
|
||||||
color: var(--color-meta-light);
|
color: var(--color-meta-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.credit-amount--bold
|
||||||
|
{
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
#main-content
|
#main-content
|
||||||
{
|
{
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
|
|
@ -26,4 +26,6 @@
|
||||||
@import "component/_divider.scss";
|
@import "component/_divider.scss";
|
||||||
@import "component/_checkbox.scss";
|
@import "component/_checkbox.scss";
|
||||||
@import "component/_radio.scss";
|
@import "component/_radio.scss";
|
||||||
|
@import "component/_shapeshift.scss";
|
||||||
|
@import "component/_spinner.scss";
|
||||||
@import "page/_show.scss";
|
@import "page/_show.scss";
|
||||||
|
|
|
@ -98,4 +98,4 @@ $button-focus-shift: 12%;
|
||||||
.button--submit {
|
.button--submit {
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
line-height: 0;
|
line-height: 0;
|
||||||
}
|
}
|
|
@ -26,6 +26,7 @@
|
||||||
.card__actions {
|
.card__actions {
|
||||||
padding: 0 var(--card-padding);
|
padding: 0 var(--card-padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
.card--small {
|
.card--small {
|
||||||
.card__title-primary,
|
.card__title-primary,
|
||||||
.card__title-identity,
|
.card__title-identity,
|
||||||
|
@ -50,7 +51,7 @@
|
||||||
.card__actions {
|
.card__actions {
|
||||||
margin-top: var(--card-margin);
|
margin-top: var(--card-margin);
|
||||||
margin-bottom: var(--card-margin);
|
margin-bottom: var(--card-margin);
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
||||||
}
|
}
|
||||||
.card__actions--bottom {
|
.card__actions--bottom {
|
||||||
|
@ -72,6 +73,18 @@
|
||||||
margin-bottom: var(--card-margin);
|
margin-bottom: var(--card-margin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card__actions--only-vertical {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card__content--extra-vertical-space {
|
||||||
|
margin: $spacing-vertical 0;
|
||||||
|
}
|
||||||
|
|
||||||
$font-size-subtext-multiple: 0.82;
|
$font-size-subtext-multiple: 0.82;
|
||||||
.card__subtext {
|
.card__subtext {
|
||||||
color: var(--color-meta-light);
|
color: var(--color-meta-light);
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
|
|
||||||
.channel-name {
|
.channel-name {
|
||||||
display: inline-block;
|
display: inline-flex;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-overflow: ellipsis
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this shouldn't know about the card width
|
// this shouldn't know about the card width
|
||||||
|
|
|
@ -56,6 +56,11 @@
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
padding-right: 5px;
|
padding-right: 5px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
font-family: "Consolas", "Lucida Console", "Adobe Source Code Pro", monospace;
|
||||||
|
|
||||||
|
&.input-copyable--with-copy-btn {
|
||||||
|
width: 85%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
input[readonly] {
|
input[readonly] {
|
||||||
|
|
|
@ -43,6 +43,8 @@
|
||||||
|
|
||||||
.wunderbar--active .icon-search { color: var(--color-primary); }
|
.wunderbar--active .icon-search { color: var(--color-primary); }
|
||||||
|
|
||||||
|
// below styles should be inside the common input styling
|
||||||
|
// will come back to this with the redesign - sean
|
||||||
.wunderbar__input {
|
.wunderbar__input {
|
||||||
background: var(--search-bg);
|
background: var(--search-bg);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
40
src/renderer/scss/component/_shapeshift.scss
Normal file
40
src/renderer/scss/component/_shapeshift.scss
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
// Can't think of a better way to do this
|
||||||
|
// The initial shapeshift form is 311px tall
|
||||||
|
// the .shapeshift__initial-wrapper class is only added when the form is being loaded
|
||||||
|
// Once the form is rendered, there is a very smooth transition because the height doesn't change
|
||||||
|
.shapeshift__wrapper.shapeshift__initial-wrapper {
|
||||||
|
min-height: 346px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shapeshift__content {
|
||||||
|
.spinner {
|
||||||
|
margin-top: $spacing-vertical * 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.shapeshift__tx-info {
|
||||||
|
min-height: 55px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shapeshift__deposit-address-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
* {
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this should be pulled out into it's own styling when we add more qr codes
|
||||||
|
.shapeshift__qrcode {
|
||||||
|
// don't use a variable here. adds a white border for easier reading in dark mode
|
||||||
|
// needs to stay the same no matter what theme is present
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 2px;
|
||||||
|
margin-left: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.shapeshift__link {
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
58
src/renderer/scss/component/_spinner.scss
Normal file
58
src/renderer/scss/component/_spinner.scss
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
.spinner {
|
||||||
|
position: relative;
|
||||||
|
width: 11em;
|
||||||
|
height: 11em;
|
||||||
|
margin: 20px auto;
|
||||||
|
font-size: 3px;
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
background: linear-gradient(to right, #fff 10%, rgba(255, 255, 255, 0) 50%);
|
||||||
|
animation: spin 1.4s infinite linear;
|
||||||
|
transform: translateZ(0);
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
from {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:before,
|
||||||
|
&:after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
width: 50%;
|
||||||
|
height: 50%;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 100% 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
height: 75%;
|
||||||
|
width: 75%;
|
||||||
|
margin: auto;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
background: #000;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner.spinner--dark {
|
||||||
|
background: linear-gradient(to right, var(--button-primary-bg) 10%, var(--color-bg) 50%);
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
background: var(--button-primary-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
background: var(--color-bg);
|
||||||
|
}
|
||||||
|
}
|
|
@ -46,53 +46,6 @@ video {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.video__loading-spinner {
|
|
||||||
position: relative;
|
|
||||||
width: 11em;
|
|
||||||
height: 11em;
|
|
||||||
margin: 20px auto;
|
|
||||||
font-size: 3px;
|
|
||||||
border-radius: 50%;
|
|
||||||
|
|
||||||
background: linear-gradient(to right, #ffffff 10%, rgba(255, 255, 255, 0) 50%);
|
|
||||||
animation: spin 1.4s infinite linear;
|
|
||||||
transform: translateZ(0);
|
|
||||||
|
|
||||||
@keyframes spin {
|
|
||||||
from {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:before,
|
|
||||||
&:after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
width: 50%;
|
|
||||||
height: 50%;
|
|
||||||
background: #ffffff;
|
|
||||||
border-radius: 100% 0 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
height: 75%;
|
|
||||||
width: 75%;
|
|
||||||
margin: auto;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
background: black;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.video__loading-status {
|
.video__loading-status {
|
||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
color: white;
|
color: white;
|
||||||
|
|
|
@ -11,6 +11,7 @@ import searchReducer from "redux/reducers/search";
|
||||||
import settingsReducer from "redux/reducers/settings";
|
import settingsReducer from "redux/reducers/settings";
|
||||||
import userReducer from "redux/reducers/user";
|
import userReducer from "redux/reducers/user";
|
||||||
import walletReducer from "redux/reducers/wallet";
|
import walletReducer from "redux/reducers/wallet";
|
||||||
|
import shapeShiftReducer from "redux/reducers/shape_shift";
|
||||||
import { persistStore, autoRehydrate } from "redux-persist";
|
import { persistStore, autoRehydrate } from "redux-persist";
|
||||||
import createCompressor from "redux-persist-transform-compress";
|
import createCompressor from "redux-persist-transform-compress";
|
||||||
import createFilter from "redux-persist-transform-filter";
|
import createFilter from "redux-persist-transform-filter";
|
||||||
|
@ -67,6 +68,7 @@ const reducers = redux.combineReducers({
|
||||||
settings: settingsReducer,
|
settings: settingsReducer,
|
||||||
wallet: walletReducer,
|
wallet: walletReducer,
|
||||||
user: userReducer,
|
user: userReducer,
|
||||||
|
shapeShift: shapeShiftReducer,
|
||||||
});
|
});
|
||||||
|
|
||||||
const bulkThunk = createBulkThunkMiddleware();
|
const bulkThunk = createBulkThunkMiddleware();
|
||||||
|
|
3
src/renderer/types/common.js
Normal file
3
src/renderer/types/common.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export type FormikActions = {
|
||||||
|
setSubmitting: boolean => mixed,
|
||||||
|
};
|
|
@ -1,6 +1,7 @@
|
||||||
// util for creating reducers
|
// util for creating reducers
|
||||||
// based off of redux-actions
|
// based off of redux-actions
|
||||||
// https://redux-actions.js.org/docs/api/handleAction.html#handleactions
|
// https://redux-actions.js.org/docs/api/handleAction.html#handleactions
|
||||||
|
|
||||||
export const handleActions = (actionMap, defaultState) => {
|
export const handleActions = (actionMap, defaultState) => {
|
||||||
return (state = defaultState, action) => {
|
return (state = defaultState, action) => {
|
||||||
const handler = actionMap[action.type];
|
const handler = actionMap[action.type];
|
||||||
|
|
50
src/renderer/util/shape_shift.js
Normal file
50
src/renderer/util/shape_shift.js
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
// these don't need to be exact
|
||||||
|
// shapeshift does a more thourough check on validity
|
||||||
|
// just general matches to prevent unneccesary api calls
|
||||||
|
export const coinRegexPatterns = {
|
||||||
|
BTC: /^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$/,
|
||||||
|
BCH: /^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$/,
|
||||||
|
ETH: /^(0x)?[0-9a-fA-F]{40}$/,
|
||||||
|
DASH: /^X([0-9a-zA-Z]){33}/,
|
||||||
|
LTC: /^L[a-zA-Z0-9]{26,33}$/,
|
||||||
|
XMR: /^4[0-9ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{94}$/,
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateAddress = (coinType, address) => {
|
||||||
|
if (!coinType || !address) return false;
|
||||||
|
|
||||||
|
const coinRegex = coinRegexPatterns[coinType.toUpperCase()];
|
||||||
|
if (!coinRegex) return false;
|
||||||
|
|
||||||
|
return coinRegex.test(address);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const validateShapeShiftForm = (vals, props) => {
|
||||||
|
let errors = {};
|
||||||
|
|
||||||
|
if (!vals.returnAddress) {
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isValidAddress = validateAddress(vals.originCoin, vals.returnAddress);
|
||||||
|
|
||||||
|
if (!isValidAddress) {
|
||||||
|
errors.returnAddress = `Enter a valid ${vals.originCoin} address`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
};
|
||||||
|
|
||||||
|
const exampleCoinAddresses = {
|
||||||
|
BTC: "1745oPaHeW7Fmpb1fUKTtasYfxr4zu9bwq",
|
||||||
|
BCH: "1745oPaHeW7Fmpb1fUKTtasYfxr4zu9bwq",
|
||||||
|
ETH: "0x8507cA6a274123fC8f80d929AF9D83602bC4e8cC",
|
||||||
|
DASH: "XedBP7vLPFXbS3URjrH2Z57Fg9SWftBmQ6",
|
||||||
|
LTC: "LgZivMvFMTDoqcA5weCQ2QrmRp7pa56bBk",
|
||||||
|
XMR:
|
||||||
|
"466XMeJEcowYGx7RzUJj3VDWBZgRWErVQQX6tHYbsacS5QF6v3tidE6LZZnTJgzeEh6bKEEJ6GC9jHirrUKvJwVKVj9e7jm",
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getExampleAddress = coin => {
|
||||||
|
return exampleCoinAddresses[coin];
|
||||||
|
};
|
136
yarn.lock
136
yarn.lock
|
@ -34,6 +34,13 @@
|
||||||
version "0.0.6"
|
version "0.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/7zip/-/7zip-0.0.6.tgz#9cafb171af82329490353b4816f03347aa150a30"
|
resolved "https://registry.yarnpkg.com/7zip/-/7zip-0.0.6.tgz#9cafb171af82329490353b4816f03347aa150a30"
|
||||||
|
|
||||||
|
"@segment/top-domain@^3.0.0":
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@segment/top-domain/-/top-domain-3.0.0.tgz#02e5a5a4fd42a9f6cf886b05e82f104012a3c3a7"
|
||||||
|
dependencies:
|
||||||
|
component-cookie "^1.1.2"
|
||||||
|
component-url "^0.2.1"
|
||||||
|
|
||||||
"@types/node@^7.0.18":
|
"@types/node@^7.0.18":
|
||||||
version "7.0.43"
|
version "7.0.43"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.43.tgz#a187e08495a075f200ca946079c914e1a5fe962c"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.43.tgz#a187e08495a075f200ca946079c914e1a5fe962c"
|
||||||
|
@ -146,6 +153,16 @@ amdefine@>=0.0.4:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
|
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
|
||||||
|
|
||||||
|
amplitude-js@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/amplitude-js/-/amplitude-js-4.0.0.tgz#70bbc0ec893b01d00453d3765f78bc0f32a395cc"
|
||||||
|
dependencies:
|
||||||
|
"@segment/top-domain" "^3.0.0"
|
||||||
|
blueimp-md5 "^2.10.0"
|
||||||
|
json3 "^3.3.2"
|
||||||
|
lodash "^4.17.4"
|
||||||
|
ua-parser-js "github:amplitude/ua-parser-js#ed538f1"
|
||||||
|
|
||||||
ansi-align@^2.0.0:
|
ansi-align@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f"
|
resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f"
|
||||||
|
@ -1269,6 +1286,10 @@ bluebird@^3.5.0, bluebird@^3.5.1, bluebird@~3.5.0:
|
||||||
version "3.5.1"
|
version "3.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9"
|
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9"
|
||||||
|
|
||||||
|
blueimp-md5@^2.10.0:
|
||||||
|
version "2.10.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.10.0.tgz#02f0843921f90dca14f5b8920a38593201d6964d"
|
||||||
|
|
||||||
bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
|
bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
|
||||||
version "4.11.8"
|
version "4.11.8"
|
||||||
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f"
|
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f"
|
||||||
|
@ -1886,6 +1907,16 @@ compare-version@^0.1.2:
|
||||||
version "0.1.2"
|
version "0.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/compare-version/-/compare-version-0.1.2.tgz#0162ec2d9351f5ddd59a9202cba935366a725080"
|
resolved "https://registry.yarnpkg.com/compare-version/-/compare-version-0.1.2.tgz#0162ec2d9351f5ddd59a9202cba935366a725080"
|
||||||
|
|
||||||
|
component-cookie@^1.1.2:
|
||||||
|
version "1.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/component-cookie/-/component-cookie-1.1.3.tgz#053e14a3bd7748154f55724fd39a60c01994ebed"
|
||||||
|
dependencies:
|
||||||
|
debug "*"
|
||||||
|
|
||||||
|
component-url@^0.2.1:
|
||||||
|
version "0.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/component-url/-/component-url-0.2.1.tgz#4e4f4799c43ead9fd3ce91b5a305d220208fee47"
|
||||||
|
|
||||||
compressible@~2.0.11:
|
compressible@~2.0.11:
|
||||||
version "2.0.12"
|
version "2.0.12"
|
||||||
resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.12.tgz#c59a5c99db76767e9876500e271ef63b3493bd66"
|
resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.12.tgz#c59a5c99db76767e9876500e271ef63b3493bd66"
|
||||||
|
@ -2235,6 +2266,12 @@ date-now@^0.1.4:
|
||||||
version "0.1.4"
|
version "0.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
|
resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
|
||||||
|
|
||||||
|
debug@*, debug@^3.0.0, debug@^3.1.0:
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
|
||||||
|
dependencies:
|
||||||
|
ms "2.0.0"
|
||||||
|
|
||||||
debug@2, debug@2.6.9, debug@^2.6.6:
|
debug@2, debug@2.6.9, debug@^2.6.6:
|
||||||
version "2.6.9"
|
version "2.6.9"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||||
|
@ -2253,12 +2290,6 @@ debug@^2.1.3, debug@^2.2.0, debug@^2.6.8:
|
||||||
dependencies:
|
dependencies:
|
||||||
ms "2.0.0"
|
ms "2.0.0"
|
||||||
|
|
||||||
debug@^3.0.0, debug@^3.1.0:
|
|
||||||
version "3.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
|
|
||||||
dependencies:
|
|
||||||
ms "2.0.0"
|
|
||||||
|
|
||||||
debuglog@^1.0.1:
|
debuglog@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
|
resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
|
||||||
|
@ -2424,6 +2455,10 @@ dom-serializer@0:
|
||||||
domelementtype "~1.1.1"
|
domelementtype "~1.1.1"
|
||||||
entities "~1.1.1"
|
entities "~1.1.1"
|
||||||
|
|
||||||
|
dom-walk@^0.1.0:
|
||||||
|
version "0.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018"
|
||||||
|
|
||||||
domain-browser@^1.1.1:
|
domain-browser@^1.1.1:
|
||||||
version "1.1.7"
|
version "1.1.7"
|
||||||
resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc"
|
resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc"
|
||||||
|
@ -3215,6 +3250,12 @@ flush-write-stream@^1.0.0:
|
||||||
inherits "^2.0.1"
|
inherits "^2.0.1"
|
||||||
readable-stream "^2.0.4"
|
readable-stream "^2.0.4"
|
||||||
|
|
||||||
|
for-each@^0.3.2:
|
||||||
|
version "0.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.2.tgz#2c40450b9348e97f281322593ba96704b9abd4d4"
|
||||||
|
dependencies:
|
||||||
|
is-function "~1.0.0"
|
||||||
|
|
||||||
for-in@^0.1.3:
|
for-in@^0.1.3:
|
||||||
version "0.1.8"
|
version "0.1.8"
|
||||||
resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1"
|
resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1"
|
||||||
|
@ -3259,6 +3300,14 @@ form-data@~2.3.1:
|
||||||
combined-stream "^1.0.5"
|
combined-stream "^1.0.5"
|
||||||
mime-types "^2.1.12"
|
mime-types "^2.1.12"
|
||||||
|
|
||||||
|
formik@^0.10.4:
|
||||||
|
version "0.10.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/formik/-/formik-0.10.5.tgz#6984d2f22e918c6d2264a3cb86b8582f7277faca"
|
||||||
|
dependencies:
|
||||||
|
lodash.isequal "4.5.0"
|
||||||
|
prop-types "^15.5.10"
|
||||||
|
warning "^3.0.0"
|
||||||
|
|
||||||
forwarded@~0.1.2:
|
forwarded@~0.1.2:
|
||||||
version "0.1.2"
|
version "0.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
|
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
|
||||||
|
@ -3473,6 +3522,13 @@ global-dirs@^0.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
ini "^1.3.4"
|
ini "^1.3.4"
|
||||||
|
|
||||||
|
global@~4.3.0:
|
||||||
|
version "4.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/global/-/global-4.3.2.tgz#e76989268a6c74c38908b1305b10fc0e394e9d0f"
|
||||||
|
dependencies:
|
||||||
|
min-document "^2.19.0"
|
||||||
|
process "~0.5.1"
|
||||||
|
|
||||||
globals@^9.18.0:
|
globals@^9.18.0:
|
||||||
version "9.18.0"
|
version "9.18.0"
|
||||||
resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
|
resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
|
||||||
|
@ -4060,6 +4116,10 @@ is-fullwidth-code-point@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
|
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
|
||||||
|
|
||||||
|
is-function@^1.0.1, is-function@~1.0.0:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.1.tgz#12cfb98b65b57dd3d193a3121f5f6e2f437602b5"
|
||||||
|
|
||||||
is-glob@^2.0.0, is-glob@^2.0.1:
|
is-glob@^2.0.0, is-glob@^2.0.1:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863"
|
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863"
|
||||||
|
@ -4622,6 +4682,10 @@ lodash.isempty@^4.4.0:
|
||||||
version "4.4.0"
|
version "4.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e"
|
resolved "https://registry.yarnpkg.com/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e"
|
||||||
|
|
||||||
|
lodash.isequal@4.5.0:
|
||||||
|
version "4.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
|
||||||
|
|
||||||
lodash.isplainobject@^4.0.6:
|
lodash.isplainobject@^4.0.6:
|
||||||
version "4.0.6"
|
version "4.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
|
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
|
||||||
|
@ -4901,6 +4965,12 @@ mimic-fn@^1.0.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18"
|
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18"
|
||||||
|
|
||||||
|
min-document@^2.19.0:
|
||||||
|
version "2.19.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685"
|
||||||
|
dependencies:
|
||||||
|
dom-walk "^0.1.0"
|
||||||
|
|
||||||
minimalistic-assert@^1.0.0:
|
minimalistic-assert@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3"
|
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3"
|
||||||
|
@ -5683,6 +5753,13 @@ parse-glob@^3.0.4:
|
||||||
is-extglob "^1.0.0"
|
is-extglob "^1.0.0"
|
||||||
is-glob "^2.0.0"
|
is-glob "^2.0.0"
|
||||||
|
|
||||||
|
parse-headers@^2.0.0:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.1.tgz#6ae83a7aa25a9d9b700acc28698cd1f1ed7e9536"
|
||||||
|
dependencies:
|
||||||
|
for-each "^0.3.2"
|
||||||
|
trim "0.0.1"
|
||||||
|
|
||||||
parse-json@^2.2.0:
|
parse-json@^2.2.0:
|
||||||
version "2.2.0"
|
version "2.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9"
|
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9"
|
||||||
|
@ -6116,6 +6193,10 @@ process@^0.11.10:
|
||||||
version "0.11.10"
|
version "0.11.10"
|
||||||
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
|
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
|
||||||
|
|
||||||
|
process@~0.5.1:
|
||||||
|
version "0.5.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/process/-/process-0.5.2.tgz#1638d8a8e34c2f440a91db95ab9aeb677fc185cf"
|
||||||
|
|
||||||
progress-stream@^1.1.0:
|
progress-stream@^1.1.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/progress-stream/-/progress-stream-1.2.0.tgz#2cd3cfea33ba3a89c9c121ec3347abe9ab125f77"
|
resolved "https://registry.yarnpkg.com/progress-stream/-/progress-stream-1.2.0.tgz#2cd3cfea33ba3a89c9c121ec3347abe9ab125f77"
|
||||||
|
@ -6220,10 +6301,21 @@ q@^1.1.2:
|
||||||
version "1.5.1"
|
version "1.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
|
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
|
||||||
|
|
||||||
|
qr.js@0.0.0:
|
||||||
|
version "0.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/qr.js/-/qr.js-0.0.0.tgz#cace86386f59a0db8050fa90d9b6b0e88a1e364f"
|
||||||
|
|
||||||
qrcode-terminal@~0.11.0:
|
qrcode-terminal@~0.11.0:
|
||||||
version "0.11.0"
|
version "0.11.0"
|
||||||
resolved "https://registry.yarnpkg.com/qrcode-terminal/-/qrcode-terminal-0.11.0.tgz#ffc6c28a2fc0bfb47052b47e23f4f446a5fbdb9e"
|
resolved "https://registry.yarnpkg.com/qrcode-terminal/-/qrcode-terminal-0.11.0.tgz#ffc6c28a2fc0bfb47052b47e23f4f446a5fbdb9e"
|
||||||
|
|
||||||
|
qrcode.react@^0.7.2:
|
||||||
|
version "0.7.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/qrcode.react/-/qrcode.react-0.7.2.tgz#72a5718fd56baafe15c2c153fe436628d83aa286"
|
||||||
|
dependencies:
|
||||||
|
prop-types "^15.5.8"
|
||||||
|
qr.js "0.0.0"
|
||||||
|
|
||||||
qs@6.5.1, qs@~6.5.1:
|
qs@6.5.1, qs@~6.5.1:
|
||||||
version "6.5.1"
|
version "6.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8"
|
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8"
|
||||||
|
@ -6772,7 +6864,7 @@ repeating@^2.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-finite "^1.0.0"
|
is-finite "^1.0.0"
|
||||||
|
|
||||||
request@2, request@^2.74.0, request@~2.83.0:
|
request@2, request@^2.55.0, request@^2.74.0, request@~2.83.0:
|
||||||
version "2.83.0"
|
version "2.83.0"
|
||||||
resolved "https://registry.yarnpkg.com/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356"
|
resolved "https://registry.yarnpkg.com/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -7087,6 +7179,13 @@ shallow-clone@^0.1.2:
|
||||||
lazy-cache "^0.2.3"
|
lazy-cache "^0.2.3"
|
||||||
mixin-object "^2.0.1"
|
mixin-object "^2.0.1"
|
||||||
|
|
||||||
|
shapeshift.io@^1.3.1:
|
||||||
|
version "1.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/shapeshift.io/-/shapeshift.io-1.3.1.tgz#939f7d89e6a93fad4b556567d3fcdab45d5cc021"
|
||||||
|
dependencies:
|
||||||
|
request "^2.55.0"
|
||||||
|
xhr "^2.0.1"
|
||||||
|
|
||||||
shebang-command@^1.2.0:
|
shebang-command@^1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
|
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
|
||||||
|
@ -7685,6 +7784,10 @@ trim-right@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
|
resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
|
||||||
|
|
||||||
|
trim@0.0.1:
|
||||||
|
version "0.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd"
|
||||||
|
|
||||||
"true-case-path@^1.0.2":
|
"true-case-path@^1.0.2":
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-1.0.2.tgz#7ec91130924766c7f573be3020c34f8fdfd00d62"
|
resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-1.0.2.tgz#7ec91130924766c7f573be3020c34f8fdfd00d62"
|
||||||
|
@ -7734,6 +7837,10 @@ ua-parser-js@^0.7.9:
|
||||||
version "0.7.17"
|
version "0.7.17"
|
||||||
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.17.tgz#e9ec5f9498b9ec910e7ae3ac626a805c4d09ecac"
|
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.17.tgz#e9ec5f9498b9ec910e7ae3ac626a805c4d09ecac"
|
||||||
|
|
||||||
|
"ua-parser-js@github:amplitude/ua-parser-js#ed538f1":
|
||||||
|
version "0.7.10"
|
||||||
|
resolved "https://codeload.github.com/amplitude/ua-parser-js/tar.gz/ed538f16f5c6ecd8357da989b617d4f156dcf35d"
|
||||||
|
|
||||||
uglify-js@3.2.x:
|
uglify-js@3.2.x:
|
||||||
version "3.2.1"
|
version "3.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.2.1.tgz#d6427fd45a25fefc5d196689c0c772a6915e10fe"
|
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.2.1.tgz#d6427fd45a25fefc5d196689c0c772a6915e10fe"
|
||||||
|
@ -7991,6 +8098,12 @@ vm-browserify@0.0.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
indexof "0.0.1"
|
indexof "0.0.1"
|
||||||
|
|
||||||
|
warning@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/warning/-/warning-3.0.0.tgz#32e5377cb572de4ab04753bdf8821c01ed605b7c"
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.0.0"
|
||||||
|
|
||||||
watchpack@^1.4.0:
|
watchpack@^1.4.0:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.4.0.tgz#4a1472bcbb952bd0a9bb4036801f954dfb39faac"
|
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.4.0.tgz#4a1472bcbb952bd0a9bb4036801f954dfb39faac"
|
||||||
|
@ -8202,6 +8315,15 @@ xdg-basedir@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"
|
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"
|
||||||
|
|
||||||
|
xhr@^2.0.1:
|
||||||
|
version "2.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.4.0.tgz#e16e66a45f869861eeefab416d5eff722dc40993"
|
||||||
|
dependencies:
|
||||||
|
global "~4.3.0"
|
||||||
|
is-function "^1.0.1"
|
||||||
|
parse-headers "^2.0.0"
|
||||||
|
xtend "^4.0.0"
|
||||||
|
|
||||||
xml-char-classes@^1.0.0:
|
xml-char-classes@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/xml-char-classes/-/xml-char-classes-1.0.0.tgz#64657848a20ffc5df583a42ad8a277b4512bbc4d"
|
resolved "https://registry.yarnpkg.com/xml-char-classes/-/xml-char-classes-1.0.0.tgz#64657848a20ffc5df583a42ad8a277b4512bbc4d"
|
||||||
|
|
Loading…
Reference in a new issue