mirror of
https://github.com/status-im/MyCrypto.git
synced 2025-02-22 15:58:31 +00:00
merge develop into 'swap_part_3'
This commit is contained in:
commit
06ae70988a
@ -1,9 +1,7 @@
|
||||
{
|
||||
"extends": ["eslint:recommended", "plugin:react/recommended"],
|
||||
"parser": "babel-eslint",
|
||||
"plugins": [
|
||||
"react"
|
||||
],
|
||||
"plugins": ["react"],
|
||||
"parserOptions": {
|
||||
"ecmaFeatures": {
|
||||
"jsx": true,
|
||||
@ -19,7 +17,7 @@
|
||||
},
|
||||
"rules": {
|
||||
"comma-dangle": 1,
|
||||
"quotes": [ 1, "single" ],
|
||||
"quotes": [1, "single"],
|
||||
"no-undef": 1,
|
||||
"global-strict": 0,
|
||||
"no-extra-semi": 1,
|
||||
@ -32,6 +30,8 @@
|
||||
"react/jsx-uses-react": 1
|
||||
},
|
||||
"globals": {
|
||||
"SyntheticInputEvent": false
|
||||
"SyntheticInputEvent": false,
|
||||
"SyntheticKeyboardEvent": false,
|
||||
"Generator": false
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,30 @@
|
||||
// @flow
|
||||
import { setLanguage } from 'translations';
|
||||
export const CONFIG_LANGUAGE_CHANGE = 'CONFIG_LANGUAGE_CHANGE';
|
||||
export const CONFIG_NODE_CHANGE = 'CONFIG_NODE_CHANGE';
|
||||
|
||||
export const CHANGE_LANGUAGE = (value: any) => {
|
||||
setLanguage(value.sign);
|
||||
return {
|
||||
type: CONFIG_LANGUAGE_CHANGE,
|
||||
value
|
||||
};
|
||||
export type ChangeNodeAction = {
|
||||
type: 'CONFIG_NODE_CHANGE',
|
||||
// FIXME $keyof?
|
||||
value: string
|
||||
};
|
||||
|
||||
export const CHANGE_NODE = (value: any) =>
|
||||
Object({
|
||||
type: CONFIG_NODE_CHANGE,
|
||||
value
|
||||
});
|
||||
export type ChangeLanguageAction = {
|
||||
type: 'CONFIG_LANGUAGE_CHANGE',
|
||||
value: string
|
||||
};
|
||||
|
||||
export type ConfigAction = ChangeNodeAction | ChangeLanguageAction;
|
||||
|
||||
export function changeLanguage(sign: string) {
|
||||
setLanguage(sign);
|
||||
return {
|
||||
type: 'CONFIG_LANGUAGE_CHANGE',
|
||||
value: sign
|
||||
};
|
||||
}
|
||||
|
||||
export function changeNode(value: string): ChangeNodeAction {
|
||||
return {
|
||||
type: 'CONFIG_NODE_CHANGE',
|
||||
value
|
||||
};
|
||||
}
|
||||
|
44
common/actions/wallet.js
Normal file
44
common/actions/wallet.js
Normal file
@ -0,0 +1,44 @@
|
||||
// @flow
|
||||
import type { PrivateKeyUnlockParams } from 'libs/wallet/privkey';
|
||||
import BaseWallet from 'libs/wallet/base';
|
||||
|
||||
export type UnlockPrivateKeyAction = {
|
||||
type: 'WALLET_UNLOCK_PRIVATE_KEY',
|
||||
payload: PrivateKeyUnlockParams
|
||||
};
|
||||
|
||||
export type SaveWalletAction = {
|
||||
type: 'WALLET_SAVE',
|
||||
payload: BaseWallet
|
||||
};
|
||||
|
||||
export type InitWalletAction = {
|
||||
type: 'WALLET_INIT'
|
||||
};
|
||||
|
||||
export type WalletAction =
|
||||
| UnlockPrivateKeyAction
|
||||
| SaveWalletAction
|
||||
| InitWalletAction;
|
||||
|
||||
export function unlockPrivateKey(
|
||||
value: PrivateKeyUnlockParams
|
||||
): UnlockPrivateKeyAction {
|
||||
return {
|
||||
type: 'WALLET_UNLOCK_PRIVATE_KEY',
|
||||
payload: value
|
||||
};
|
||||
}
|
||||
|
||||
export function saveWallet(value: BaseWallet): SaveWalletAction {
|
||||
return {
|
||||
type: 'WALLET_SAVE',
|
||||
payload: value
|
||||
};
|
||||
}
|
||||
|
||||
export function initWallet(): InitWalletAction {
|
||||
return {
|
||||
type: 'WALLET_INIT'
|
||||
};
|
||||
}
|
@ -1,31 +1,24 @@
|
||||
// @flow
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import TabsOptions from './components/TabsOptions';
|
||||
import { Link } from 'react-router';
|
||||
import { Dropdown } from 'components/ui';
|
||||
import { languages, nodeList } from '../../config/data';
|
||||
import { languages, NODES } from '../../config/data';
|
||||
|
||||
export default class Header extends Component {
|
||||
static propTypes = {
|
||||
location: PropTypes.object,
|
||||
props: {
|
||||
languageSelection: string,
|
||||
nodeSelection: string,
|
||||
|
||||
// Language DropDown
|
||||
changeLanguage: PropTypes.func,
|
||||
languageSelection: PropTypes.object,
|
||||
|
||||
// Node Dropdown
|
||||
changeNode: PropTypes.func,
|
||||
nodeSelection: PropTypes.object
|
||||
changeLanguage: (sign: string) => any,
|
||||
changeNode: (key: string) => any
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
languageSelection,
|
||||
changeLanguage,
|
||||
changeNode,
|
||||
nodeSelection
|
||||
} = this.props;
|
||||
const { languageSelection, changeNode, nodeSelection } = this.props;
|
||||
const selectedLanguage =
|
||||
languages.find(l => l.sign === languageSelection) || languages[0];
|
||||
const selectedNode = NODES[nodeSelection];
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -48,10 +41,10 @@ export default class Header extends Component {
|
||||
</span>
|
||||
|
||||
<Dropdown
|
||||
ariaLabel={`change language. current language ${languageSelection.name}`}
|
||||
ariaLabel={`change language. current language ${selectedLanguage.name}`}
|
||||
options={languages}
|
||||
formatTitle={o => o.name}
|
||||
value={languageSelection}
|
||||
value={selectedLanguage}
|
||||
extra={[
|
||||
<li key={'separator'} role="separator" className="divider" />,
|
||||
<li key={'disclaimer'}>
|
||||
@ -60,23 +53,23 @@ export default class Header extends Component {
|
||||
</a>
|
||||
</li>
|
||||
]}
|
||||
onChange={changeLanguage}
|
||||
onChange={this.changeLanguage}
|
||||
/>
|
||||
|
||||
<Dropdown
|
||||
ariaLabel={`change node. current node ${nodeSelection.name} node by ${nodeSelection.service}`}
|
||||
options={nodeList}
|
||||
ariaLabel={`change node. current node ${selectedNode.network} node by ${selectedNode.service}`}
|
||||
options={Object.keys(NODES)}
|
||||
formatTitle={o => [
|
||||
o.name,
|
||||
NODES[o].network,
|
||||
' ',
|
||||
<small key="service">({o.service})</small>
|
||||
<small key="service">
|
||||
({NODES[o].service})
|
||||
</small>
|
||||
]}
|
||||
value={nodeSelection}
|
||||
extra={
|
||||
<li>
|
||||
<a onClick={() => {}}>
|
||||
Add Custom Node
|
||||
</a>
|
||||
<a onClick={() => {}}>Add Custom Node</a>
|
||||
</li>
|
||||
}
|
||||
onChange={changeNode}
|
||||
@ -86,8 +79,11 @@ export default class Header extends Component {
|
||||
</section>
|
||||
|
||||
<TabsOptions {...this.props} />
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
changeLanguage = (value: { sign: string }) => {
|
||||
this.props.changeLanguage(value.sign);
|
||||
};
|
||||
}
|
||||
|
63
common/components/WalletDecrypt/Keystore.jsx
Normal file
63
common/components/WalletDecrypt/Keystore.jsx
Normal file
@ -0,0 +1,63 @@
|
||||
import React, { Component } from 'react';
|
||||
import translate from 'translations';
|
||||
import wallet from 'ethereumjs-wallet';
|
||||
import ethUtil from 'ethereumjs-util';
|
||||
|
||||
export default class KeystoreDecrypt extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
handleFileSelection = event => {
|
||||
const fileReader = new FileReader();
|
||||
const inputFile = event.target.files[0];
|
||||
|
||||
fileReader.onload = () => {
|
||||
try {
|
||||
const keyStoreString = fileReader.result;
|
||||
const decryptedWallet = wallet.fromV3(
|
||||
keyStoreString,
|
||||
'asdfasdfasdf',
|
||||
true
|
||||
);
|
||||
const privateHex = ethUtil.bufferToHex(decryptedWallet._privKey);
|
||||
const publicHex = ethUtil.bufferToHex(
|
||||
ethUtil.privateToAddress(decryptedWallet._privKey)
|
||||
);
|
||||
console.log(privateHex, publicHex); // TODO: Remove console log, it's only here to let Travis pass
|
||||
} catch (e) {
|
||||
console.error('Could not parse Keystore file.', e);
|
||||
}
|
||||
};
|
||||
|
||||
fileReader.readAsText(inputFile, 'utf-8');
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<section className="col-md-4 col-sm-6">
|
||||
<div id="selectedUploadKey">
|
||||
<h4>{translate('ADD_Radio_2_alt')}</h4>
|
||||
|
||||
<div className="form-group">
|
||||
<input
|
||||
type="file"
|
||||
id="fselector"
|
||||
onChange={this.handleFileSelection}
|
||||
/>
|
||||
<label htmlFor="fselector">
|
||||
<a
|
||||
className="btn-file marg-v-sm"
|
||||
id="aria1"
|
||||
tabIndex="0"
|
||||
role="button"
|
||||
>
|
||||
{translate('ADD_Radio_2_short')}
|
||||
</a>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
}
|
27
common/components/WalletDecrypt/LedgerNano.jsx
Normal file
27
common/components/WalletDecrypt/LedgerNano.jsx
Normal file
@ -0,0 +1,27 @@
|
||||
import React, { Component } from 'react';
|
||||
import translate from 'translations';
|
||||
|
||||
export default class LedgerNanoSDecrypt extends Component {
|
||||
render() {
|
||||
return (
|
||||
<section className="col-md-4 col-sm-6">
|
||||
<div id="selectedUploadKey">
|
||||
<h4>{translate('ADD_Radio_2_alt')}</h4>
|
||||
|
||||
<div className="form-group">
|
||||
<input type="file" id="fselector" />
|
||||
|
||||
<a
|
||||
className="btn-file marg-v-sm"
|
||||
id="aria1"
|
||||
tabIndex="0"
|
||||
role="button"
|
||||
>
|
||||
{translate('ADD_Radio_2_short')}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
}
|
27
common/components/WalletDecrypt/Mnemonic.jsx
Normal file
27
common/components/WalletDecrypt/Mnemonic.jsx
Normal file
@ -0,0 +1,27 @@
|
||||
import React, { Component } from 'react';
|
||||
import translate from 'translations';
|
||||
|
||||
export default class MnemonicDecrypt extends Component {
|
||||
render() {
|
||||
return (
|
||||
<section className="col-md-4 col-sm-6">
|
||||
<div id="selectedUploadKey">
|
||||
<h4>{translate('ADD_Radio_2_alt')}</h4>
|
||||
|
||||
<div className="form-group">
|
||||
<input type="file" id="fselector" />
|
||||
|
||||
<a
|
||||
className="btn-file marg-v-sm"
|
||||
id="aria1"
|
||||
tabIndex="0"
|
||||
role="button"
|
||||
>
|
||||
{translate('ADD_Radio_2_short')}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
}
|
101
common/components/WalletDecrypt/PrivateKey.jsx
Normal file
101
common/components/WalletDecrypt/PrivateKey.jsx
Normal file
@ -0,0 +1,101 @@
|
||||
// @flow
|
||||
import React, { Component } from 'react';
|
||||
import translate from 'translations';
|
||||
import { isValidPrivKey } from 'libs/validators';
|
||||
import type { PrivateKeyUnlockParams } from 'libs/wallet/privkey';
|
||||
|
||||
export type PrivateKeyValue = PrivateKeyUnlockParams & {
|
||||
valid: boolean
|
||||
};
|
||||
|
||||
function fixPkey(key) {
|
||||
if (key.indexOf('0x') === 0) {
|
||||
return key.slice(2);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
export default class PrivateKeyDecrypt extends Component {
|
||||
props: {
|
||||
value: PrivateKeyUnlockParams,
|
||||
onChange: (value: PrivateKeyUnlockParams) => void,
|
||||
onUnlock: () => void
|
||||
};
|
||||
|
||||
render() {
|
||||
const { key, password } = this.props.value;
|
||||
const fixedPkey = fixPkey(key);
|
||||
const isValid = isValidPrivKey(fixedPkey.length);
|
||||
const isPassRequired = fixedPkey.length > 64;
|
||||
|
||||
return (
|
||||
<section className="col-md-4 col-sm-6">
|
||||
<div id="selectedTypeKey">
|
||||
<h4>
|
||||
{translate('ADD_Radio_3')}
|
||||
</h4>
|
||||
<div className="form-group">
|
||||
<textarea
|
||||
id="aria-private-key"
|
||||
className={`form-control ${isValid ? 'is-valid' : 'is-invalid'}`}
|
||||
value={key}
|
||||
onChange={this.onPkeyChange}
|
||||
onKeyDown={this.onKeyDown}
|
||||
placeholder={translate('x_PrivKey2')}
|
||||
rows="4"
|
||||
/>
|
||||
</div>
|
||||
{isValid &&
|
||||
isPassRequired &&
|
||||
<div className="form-group">
|
||||
<p>
|
||||
{translate('ADD_Label_3')}
|
||||
</p>
|
||||
<input
|
||||
className={`form-control ${password.length > 0
|
||||
? 'is-valid'
|
||||
: 'is-invalid'}`}
|
||||
value={password}
|
||||
onChange={this.onPasswordChange}
|
||||
onKeyDown={this.onKeyDown}
|
||||
placeholder={translate('x_Password')}
|
||||
type="password"
|
||||
/>
|
||||
</div>}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
onPkeyChange = (e: SyntheticInputEvent) => {
|
||||
const fixedPkey = fixPkey(e.target.value);
|
||||
const isValid = isValidPrivKey(fixedPkey.length);
|
||||
const isPassRequired = fixedPkey.length > 64;
|
||||
const valid =
|
||||
isValid && (isPassRequired ? this.props.value.password.length > 0 : true);
|
||||
|
||||
this.props.onChange({ ...this.props.value, key: e.target.value, valid });
|
||||
};
|
||||
|
||||
onPasswordChange = (e: SyntheticInputEvent) => {
|
||||
const fixedPkey = fixPkey(this.props.value.key);
|
||||
const isValid = isValidPrivKey(fixedPkey.length);
|
||||
const isPassRequired = fixedPkey.length > 64;
|
||||
const valid =
|
||||
isValid && (isPassRequired ? e.target.value.length > 0 : true);
|
||||
|
||||
this.props.onChange({
|
||||
...this.props.value,
|
||||
password: e.target.value,
|
||||
valid
|
||||
});
|
||||
};
|
||||
|
||||
onKeyDown = (e: SyntheticKeyboardEvent) => {
|
||||
if (e.keyCode === 13) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.props.onUnlock();
|
||||
}
|
||||
};
|
||||
}
|
27
common/components/WalletDecrypt/Trezor.jsx
Normal file
27
common/components/WalletDecrypt/Trezor.jsx
Normal file
@ -0,0 +1,27 @@
|
||||
import React, { Component } from 'react';
|
||||
import translate from 'translations';
|
||||
|
||||
export default class TrezorDecrypt extends Component {
|
||||
render() {
|
||||
return (
|
||||
<section className="col-md-4 col-sm-6">
|
||||
<div id="selectedUploadKey">
|
||||
<h4>{translate('ADD_Radio_2_alt')}</h4>
|
||||
|
||||
<div className="form-group">
|
||||
<input type="file" id="fselector" />
|
||||
|
||||
<a
|
||||
className="btn-file marg-v-sm"
|
||||
id="aria1"
|
||||
tabIndex="0"
|
||||
role="button"
|
||||
>
|
||||
{translate('ADD_Radio_2_short')}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
}
|
27
common/components/WalletDecrypt/ViewOnly.jsx
Normal file
27
common/components/WalletDecrypt/ViewOnly.jsx
Normal file
@ -0,0 +1,27 @@
|
||||
import React, { Component } from 'react';
|
||||
import translate from 'translations';
|
||||
|
||||
export default class ViewOnlyDecrypt extends Component {
|
||||
render() {
|
||||
return (
|
||||
<section className="col-md-4 col-sm-6">
|
||||
<div id="selectedUploadKey">
|
||||
<h4>{translate('ADD_Radio_2_alt')}</h4>
|
||||
|
||||
<div className="form-group">
|
||||
<input type="file" id="fselector" />
|
||||
|
||||
<a
|
||||
className="btn-file marg-v-sm"
|
||||
id="aria1"
|
||||
tabIndex="0"
|
||||
role="button"
|
||||
>
|
||||
{translate('ADD_Radio_2_short')}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
}
|
163
common/components/WalletDecrypt/index.jsx
Normal file
163
common/components/WalletDecrypt/index.jsx
Normal file
@ -0,0 +1,163 @@
|
||||
// @flow
|
||||
import React, { Component } from 'react';
|
||||
import translate from 'translations';
|
||||
import KeystoreDecrypt from './Keystore';
|
||||
import PrivateKeyDecrypt from './PrivateKey';
|
||||
import type { PrivateKeyValue } from './PrivateKey';
|
||||
import MnemonicDecrypt from './Mnemonic';
|
||||
import LedgerNanoSDecrypt from './LedgerNano';
|
||||
import TrezorDecrypt from './Trezor';
|
||||
import ViewOnlyDecrypt from './ViewOnly';
|
||||
import map from 'lodash/map';
|
||||
import { unlockPrivateKey } from 'actions/wallet';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
const WALLETS = {
|
||||
'keystore-file': {
|
||||
lid: 'x_Keystore2',
|
||||
component: KeystoreDecrypt,
|
||||
initialParams: {}
|
||||
},
|
||||
'private-key': {
|
||||
lid: 'x_PrivKey2',
|
||||
component: PrivateKeyDecrypt,
|
||||
initialParams: {
|
||||
key: '',
|
||||
password: ''
|
||||
},
|
||||
unlock: unlockPrivateKey
|
||||
},
|
||||
'mnemonic-phrase': {
|
||||
lid: 'x_Mnemonic',
|
||||
component: MnemonicDecrypt
|
||||
},
|
||||
'ledger-nano-s': {
|
||||
lid: 'x_Ledger',
|
||||
component: LedgerNanoSDecrypt
|
||||
},
|
||||
trezor: {
|
||||
lid: 'x_Trezor',
|
||||
component: TrezorDecrypt
|
||||
},
|
||||
'view-only': {
|
||||
lid: 'View with Address Only',
|
||||
component: ViewOnlyDecrypt
|
||||
}
|
||||
};
|
||||
|
||||
type UnlockParams = {} | PrivateKeyValue;
|
||||
|
||||
type State = {
|
||||
selectedWalletKey: string,
|
||||
value: UnlockParams
|
||||
};
|
||||
|
||||
export class WalletDecrypt extends Component {
|
||||
props: {
|
||||
// FIXME
|
||||
dispatch: (action: any) => void
|
||||
};
|
||||
state: State = {
|
||||
selectedWalletKey: 'keystore-file',
|
||||
value: WALLETS['keystore-file'].initialParams
|
||||
};
|
||||
|
||||
getDecryptionComponent() {
|
||||
const { selectedWalletKey, value } = this.state;
|
||||
const selectedWallet = WALLETS[selectedWalletKey];
|
||||
|
||||
if (!selectedWallet) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<selectedWallet.component
|
||||
value={value}
|
||||
onChange={this.onChange}
|
||||
onUnlock={this.onUnlock}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
buildWalletOptions() {
|
||||
return map(WALLETS, (wallet, key) => {
|
||||
const isSelected = this.state.selectedWalletKey === key;
|
||||
|
||||
return (
|
||||
<label className="radio" key={key}>
|
||||
<input
|
||||
aria-flowto={`aria-${key}`}
|
||||
aria-labelledby={`${key}-label`}
|
||||
type="radio"
|
||||
name="decryption-choice-radio-group"
|
||||
value={key}
|
||||
checked={isSelected}
|
||||
onChange={this.handleDecryptionChoiceChange}
|
||||
/>
|
||||
<span id={`${key}-label`}>
|
||||
{translate(wallet.lid)}
|
||||
</span>
|
||||
</label>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
handleDecryptionChoiceChange = (event: SyntheticInputEvent) => {
|
||||
const wallet = WALLETS[event.target.value];
|
||||
|
||||
if (!wallet) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
selectedWalletKey: event.target.value,
|
||||
value: wallet.initialParams
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const decryptionComponent = this.getDecryptionComponent();
|
||||
|
||||
return (
|
||||
<article className="well decrypt-drtv row">
|
||||
<section className="col-md-4 col-sm-6">
|
||||
<h4>
|
||||
{translate('decrypt_Access')}
|
||||
</h4>
|
||||
|
||||
{this.buildWalletOptions()}
|
||||
</section>
|
||||
|
||||
{decryptionComponent}
|
||||
{!!this.state.value.valid &&
|
||||
<section className="col-md-4 col-sm-6">
|
||||
<h4 id="uploadbtntxt-wallet">
|
||||
{translate('ADD_Label_6')}
|
||||
</h4>
|
||||
<div className="form-group">
|
||||
<a
|
||||
tabIndex="0"
|
||||
role="button"
|
||||
className="btn btn-primary btn-block"
|
||||
onClick={this.onUnlock}
|
||||
>
|
||||
{translate('ADD_Label_6_short')}
|
||||
</a>
|
||||
</div>
|
||||
</section>}
|
||||
</article>
|
||||
);
|
||||
}
|
||||
|
||||
onChange = (value: UnlockParams) => {
|
||||
this.setState({ value });
|
||||
};
|
||||
|
||||
onUnlock = () => {
|
||||
this.props.dispatch(
|
||||
WALLETS[this.state.selectedWalletKey].unlock(this.state.value)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default connect()(WalletDecrypt);
|
@ -1,3 +1,5 @@
|
||||
export Header from './Header';
|
||||
export Footer from './Footer';
|
||||
export Root from './Root';
|
||||
// @flow
|
||||
|
||||
export { default as Header } from './Header';
|
||||
export { default as Footer } from './Footer';
|
||||
export { default as Root } from './Root';
|
||||
|
@ -1,26 +1,25 @@
|
||||
// @flow
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export default class DropdownComponent extends Component {
|
||||
static propTypes = {
|
||||
value: PropTypes.object.isRequired,
|
||||
options: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
ariaLabel: PropTypes.string.isRequired,
|
||||
formatTitle: PropTypes.func.isRequired,
|
||||
extra: PropTypes.node,
|
||||
onChange: PropTypes.func.isRequired
|
||||
};
|
||||
type Props<T> = {
|
||||
value: T,
|
||||
options: T[],
|
||||
ariaLabel: string,
|
||||
formatTitle: (option: T) => any,
|
||||
extra?: any,
|
||||
onChange: (value: T) => void
|
||||
};
|
||||
|
||||
// FIXME
|
||||
props: {
|
||||
value: any,
|
||||
options: any[],
|
||||
ariaLabel: string,
|
||||
formatTitle: (option: any) => any,
|
||||
extra?: any,
|
||||
onChange: (value: any) => void
|
||||
};
|
||||
type State = {
|
||||
expanded: boolean
|
||||
};
|
||||
|
||||
export default class DropdownComponent<T: *> extends Component<
|
||||
void,
|
||||
Props<T>,
|
||||
State
|
||||
> {
|
||||
props: Props<T>;
|
||||
|
||||
state = {
|
||||
expanded: false
|
||||
|
@ -2,11 +2,18 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import translate from 'translations';
|
||||
import WalletDecrypt from 'components/WalletDecrypt';
|
||||
import BaseWallet from 'libs/wallet/base';
|
||||
import { connect } from 'react-redux';
|
||||
import type { State } from 'reducers';
|
||||
|
||||
export default class UnlockHeader extends React.Component {
|
||||
props: {
|
||||
title: string
|
||||
};
|
||||
type Props = {
|
||||
title: string,
|
||||
wallet: BaseWallet
|
||||
};
|
||||
|
||||
export class UnlockHeader extends React.Component {
|
||||
props: Props;
|
||||
static propTypes = {
|
||||
title: PropTypes.string.isRequired
|
||||
};
|
||||
@ -17,17 +24,33 @@ export default class UnlockHeader extends React.Component {
|
||||
expanded: true
|
||||
};
|
||||
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
if (this.props.wallet && this.props.wallet !== prevProps.wallet) {
|
||||
this.setState({ expanded: false });
|
||||
}
|
||||
|
||||
// not sure if could happen
|
||||
if (!this.props.wallet && this.props.wallet !== prevProps.wallet) {
|
||||
this.setState({ expanded: true });
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<article className="collapse-container">
|
||||
<div onClick={this.toggleExpanded}>
|
||||
<a className="collapse-button">
|
||||
<span>{this.state.expanded ? '-' : '+'}</span>
|
||||
<span>
|
||||
{this.state.expanded ? '-' : '+'}
|
||||
</span>
|
||||
</a>
|
||||
<h1>{translate(this.props.title)}</h1>
|
||||
<h1>
|
||||
{translate(this.props.title)}
|
||||
</h1>
|
||||
</div>
|
||||
{this.state.expanded &&
|
||||
<div>
|
||||
<WalletDecrypt />
|
||||
{/* @@if (site === 'cx' ) { <cx-wallet-decrypt-drtv></cx-wallet-decrypt-drtv> }
|
||||
@@if (site === 'mew' ) { <wallet-decrypt-drtv></wallet-decrypt-drtv> } */}
|
||||
</div>}
|
||||
@ -43,3 +66,11 @@ export default class UnlockHeader extends React.Component {
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function mapStateToProps(state: State) {
|
||||
return {
|
||||
wallet: state.wallet
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(UnlockHeader);
|
||||
|
@ -1,9 +1,14 @@
|
||||
export const donationAddressMap = {
|
||||
// @flow
|
||||
import { RPCNode } from 'libs/nodes';
|
||||
|
||||
export const DONATION_ADDRESSES_MAP = {
|
||||
BTC: '1MEWT2SGbqtz6mPCgFcnea8XmWV5Z4Wc6',
|
||||
ETH: '0x7cB57B5A97eAbe94205C07890BE4c1aD31E486A8',
|
||||
REP: '0x7cB57B5A97eAbe94205C07890BE4c1aD31E486A8'
|
||||
};
|
||||
|
||||
export const donationAddressMap = DONATION_ADDRESSES_MAP;
|
||||
|
||||
export const languages = [
|
||||
{
|
||||
sign: 'en',
|
||||
@ -99,70 +104,23 @@ export const languages = [
|
||||
}
|
||||
];
|
||||
|
||||
export const nodeList = [
|
||||
{
|
||||
export const NETWORKS = {
|
||||
ETH: {
|
||||
name: 'ETH',
|
||||
blockExplorerTX: 'https://etherscan.io/tx/[[txHash]]',
|
||||
blockExplorerAddr: 'https://etherscan.io/address/[[address]]',
|
||||
// 'type': nodes.nodeTypes.ETH,
|
||||
eip155: true,
|
||||
chainId: 1,
|
||||
// 'tokenList': require('./tokens/ethTokens.json'),
|
||||
// 'abiList': require('./abiDefinitions/ethAbi.json'),
|
||||
estimateGas: true,
|
||||
service: 'MyEtherWallet'
|
||||
// 'lib': new nodes.customNode('https://api.myetherapi.com/eth', '')
|
||||
},
|
||||
{
|
||||
name: 'ETH',
|
||||
blockExplorerTX: 'https://etherscan.io/tx/[[txHash]]',
|
||||
blockExplorerAddr: 'https://etherscan.io/address/[[address]]',
|
||||
// 'type': nodes.nodeTypes.ETH,
|
||||
eip155: true,
|
||||
chainId: 1,
|
||||
// 'tokenList': require('./tokens/ethTokens.json'),
|
||||
// 'abiList': require('./abiDefinitions/ethAbi.json'),
|
||||
estimateGas: false,
|
||||
service: 'Etherscan.io'
|
||||
// 'lib': require('./nodeHelpers/etherscan')
|
||||
},
|
||||
{
|
||||
name: 'Ropsten',
|
||||
// 'type': nodes.nodeTypes.Ropsten,
|
||||
blockExplorerTX: 'https://ropsten.etherscan.io/tx/[[txHash]]',
|
||||
blockExplorerAddr: 'https://ropsten.etherscan.io/address/[[address]]',
|
||||
eip155: true,
|
||||
chainId: 3,
|
||||
// 'tokenList': require('./tokens/ropstenTokens.json'),
|
||||
// 'abiList': require('./abiDefinitions/ropstenAbi.json'),
|
||||
estimateGas: false,
|
||||
service: 'MyEtherWallet'
|
||||
// 'lib': new nodes.customNode('https://api.myetherapi.com/rop', '')
|
||||
},
|
||||
{
|
||||
name: 'Kovan',
|
||||
// 'type': nodes.nodeTypes.Kovan,
|
||||
blockExplorerTX: 'https://kovan.etherscan.io/tx/[[txHash]]',
|
||||
blockExplorerAddr: 'https://kovan.etherscan.io/address/[[address]]',
|
||||
eip155: true,
|
||||
chainId: 42,
|
||||
// 'tokenList': require('./tokens/kovanTokens.json'),
|
||||
// 'abiList': require('./abiDefinitions/kovanAbi.json'),
|
||||
estimateGas: false,
|
||||
service: 'Etherscan.io'
|
||||
// 'lib': require('./nodeHelpers/etherscanKov')
|
||||
},
|
||||
{
|
||||
name: 'ETC',
|
||||
blockExplorerTX: 'https://gastracker.io/tx/[[txHash]]',
|
||||
blockExplorerAddr: 'https://gastracker.io/addr/[[address]]',
|
||||
// 'type': nodes.nodeTypes.ETC,
|
||||
eip155: true,
|
||||
chainId: 61,
|
||||
// 'tokenList': require('./tokens/etcTokens.json'),
|
||||
// 'abiList': require('./abiDefinitions/etcAbi.json'),
|
||||
estimateGas: false,
|
||||
service: 'Epool.io'
|
||||
// 'lib': new nodes.customNode('https://mewapi.epool.io', '')
|
||||
chainId: 1
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
export const NODES = {
|
||||
eth_mew: {
|
||||
network: 'ETH',
|
||||
lib: new RPCNode('https://api.myetherapi.com/eth'),
|
||||
service: 'MyEtherWallet',
|
||||
estimateGas: true,
|
||||
eip155: true
|
||||
// 'tokenList': require('./tokens/ethTokens.json'),
|
||||
// 'abiList': require('./abiDefinitions/ethAbi.json'),
|
||||
}
|
||||
};
|
||||
|
@ -2,86 +2,65 @@
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { Footer, Header } from 'components';
|
||||
import PropTypes from 'prop-types';
|
||||
import Notifications from './Notifications';
|
||||
|
||||
import { CHANGE_LANGUAGE, CHANGE_NODE } from 'actions/config';
|
||||
import * as actions from 'actions/config';
|
||||
|
||||
class App extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
props: {
|
||||
// FIXME
|
||||
children: any,
|
||||
location: any,
|
||||
router: any,
|
||||
isMobile: boolean,
|
||||
|
||||
static propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
location: PropTypes.object,
|
||||
handleWindowResize: PropTypes.func,
|
||||
languageSelection: string,
|
||||
nodeSelection: string,
|
||||
|
||||
router: PropTypes.object,
|
||||
isMobile: PropTypes.bool,
|
||||
changeLanguage: typeof actions.changeLanguage,
|
||||
changeNode: typeof actions.changeNode,
|
||||
handleWindowResize: () => void
|
||||
};
|
||||
|
||||
// BEGIN ACTUAL
|
||||
languageSelection: PropTypes.object,
|
||||
changeLanguage: PropTypes.func,
|
||||
render() {
|
||||
let {
|
||||
children,
|
||||
// APP
|
||||
languageSelection,
|
||||
changeLanguage,
|
||||
changeNode,
|
||||
nodeSelection
|
||||
} = this.props;
|
||||
|
||||
changeNode: PropTypes.func,
|
||||
nodeSelection: PropTypes.object,
|
||||
|
||||
showNotification: PropTypes.func
|
||||
let headerProps = {
|
||||
location,
|
||||
changeLanguage,
|
||||
languageSelection,
|
||||
changeNode,
|
||||
nodeSelection
|
||||
};
|
||||
|
||||
render() {
|
||||
let {
|
||||
children,
|
||||
// APP
|
||||
languageSelection,
|
||||
changeLanguage,
|
||||
changeNode,
|
||||
nodeSelection
|
||||
} = this.props;
|
||||
|
||||
let headerProps = {
|
||||
location,
|
||||
changeLanguage,
|
||||
languageSelection,
|
||||
changeNode,
|
||||
nodeSelection
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="page-layout">
|
||||
<main>
|
||||
<Header {...headerProps} />
|
||||
<div className="main-content">
|
||||
{React.cloneElement(children, { languageSelection })}
|
||||
</div>
|
||||
<Footer />
|
||||
</main>
|
||||
<Notifications />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="page-layout">
|
||||
<main>
|
||||
<Header {...headerProps} />
|
||||
<div className="main-content">
|
||||
{React.cloneElement(children, { languageSelection })}
|
||||
</div>
|
||||
<Footer />
|
||||
</main>
|
||||
<Notifications />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
nodeSelection: state.config.nodeSelection,
|
||||
nodeToggle: state.config.nodeToggle,
|
||||
languageSelection: state.config.languageSelection,
|
||||
languageToggle: state.config.languageToggle
|
||||
};
|
||||
return {
|
||||
nodeSelection: state.config.nodeSelection,
|
||||
nodeToggle: state.config.nodeToggle,
|
||||
languageSelection: state.config.languageSelection,
|
||||
languageToggle: state.config.languageToggle
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
// FIXME replace with actual types
|
||||
changeNode: (i: any) => {
|
||||
dispatch(CHANGE_NODE(i));
|
||||
},
|
||||
changeLanguage: (i: any) => {
|
||||
dispatch(CHANGE_LANGUAGE(i));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(App);
|
||||
export default connect(mapStateToProps, actions)(App);
|
||||
|
@ -13,6 +13,9 @@ import {
|
||||
AddressField
|
||||
} from './components';
|
||||
import pickBy from 'lodash/pickBy';
|
||||
import type { State as AppState } from 'reducers';
|
||||
import { connect } from 'react-redux';
|
||||
import BaseWallet from 'libs/wallet/base';
|
||||
// import type { Transaction } from './types';
|
||||
import customMessages from './messages';
|
||||
import { donationAddressMap } from 'config/data';
|
||||
@ -50,7 +53,8 @@ export class SendTransaction extends React.Component {
|
||||
query: {
|
||||
[string]: string
|
||||
}
|
||||
}
|
||||
},
|
||||
wallet: BaseWallet
|
||||
};
|
||||
state: State = {
|
||||
hasQueryString: false,
|
||||
@ -69,12 +73,10 @@ export class SendTransaction extends React.Component {
|
||||
if (Object.keys(queryPresets).length) {
|
||||
this.setState({ ...queryPresets, hasQueryString: true });
|
||||
}
|
||||
|
||||
this.setState(pickBy(queryPresets));
|
||||
}
|
||||
|
||||
render() {
|
||||
const unlocked = true; //wallet != null
|
||||
const unlocked = !!this.props.wallet;
|
||||
const unitReadable = 'UNITREADABLE';
|
||||
const nodeUnit = 'NODEUNIT';
|
||||
const hasEnoughBalance = false;
|
||||
@ -95,8 +97,7 @@ export class SendTransaction extends React.Component {
|
||||
return (
|
||||
<section className="container" style={{ minHeight: '50%' }}>
|
||||
<div className="tab-content">
|
||||
<main className="tab-pane active">
|
||||
|
||||
<main className="tab-pane active" ng-controller="sendTxCtrl">
|
||||
{hasQueryString &&
|
||||
<div className="alert alert-info">
|
||||
<p>
|
||||
@ -125,8 +126,7 @@ export class SendTransaction extends React.Component {
|
||||
<strong>
|
||||
Warning! You do not have enough funds to
|
||||
complete this swap.
|
||||
</strong>
|
||||
{' '}
|
||||
</strong>{' '}
|
||||
<br />
|
||||
Please add more funds or access a different wallet.
|
||||
</div>
|
||||
@ -196,7 +196,6 @@ export class SendTransaction extends React.Component {
|
||||
{' '}Send Transaction{' '}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
{'' /* <!-- / Content --> */}
|
||||
{
|
||||
@ -275,5 +274,11 @@ export class SendTransaction extends React.Component {
|
||||
});
|
||||
};
|
||||
}
|
||||
// export connected version
|
||||
export default SendTransaction;
|
||||
|
||||
function mapStateToProps(state: AppState) {
|
||||
return {
|
||||
wallet: state.wallet.inst
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(SendTransaction);
|
||||
|
@ -1,10 +1,9 @@
|
||||
// @flow
|
||||
import { donationAddressMap } from 'config/data';
|
||||
|
||||
export default [
|
||||
{
|
||||
// donation address example
|
||||
to: donationAddressMap.ETH,
|
||||
to: '0x7cB57B5A97eAbe94205C07890BE4c1aD31E486A8',
|
||||
gasLimit: 21000,
|
||||
data: '',
|
||||
msg: 'Thank you for donating to MyEtherWallet. TO THE MOON!'
|
||||
|
@ -1,126 +0,0 @@
|
||||
import React, {Component} from 'react';
|
||||
|
||||
export default class ViewWallet extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<section className="container" style={{minHeight: '50%'}}>
|
||||
<div className="tab-content">
|
||||
<article className="tab-pane active ng-scope"
|
||||
// ng-if="globalService.currentTab==globalService.tabs.viewWalletInfo.id"
|
||||
// ng-controller="viewWalletCtrl"
|
||||
>
|
||||
|
||||
<article className="collapse-container">
|
||||
<div
|
||||
// ng-click="wd = !wd"
|
||||
>
|
||||
{/*<a className="collapse-button"><span ng-show="wd" className="ng-hide">+</span><span*/}
|
||||
{/*ng-show="!wd">-</span></a>*/}
|
||||
|
||||
<h1 className="ng-scope">View Wallet Info</h1>
|
||||
|
||||
</div>
|
||||
<div
|
||||
// ng-show="!wd"
|
||||
>
|
||||
<p className="ng-scope">This allows you to download different versions
|
||||
of
|
||||
private keys and re-print your paper wallet. You may want to do this in order to
|
||||
<a
|
||||
target="_blank"
|
||||
href="http://ethereum.stackexchange.com/questions/465/how-to-import-a-plain-private-key-into-geth/">
|
||||
import
|
||||
your account into Geth/Mist</a>. If you want to check your balance, we
|
||||
recommend
|
||||
using a blockchain explorer like <a target="_blank" href="http://etherscan.io/">etherscan.io</a>.
|
||||
</p>
|
||||
{/*<wallet-decrypt-drtv/><article className="well decrypt-drtv row ng-scope" ng-controller="decryptWalletCtrl as $crtl">*/}
|
||||
<article className="well decrypt-drtv row ng-scope"
|
||||
// ng-controller="decryptWalletCtrl as $crtl"
|
||||
>
|
||||
<section className="col-md-4 col-sm-6">
|
||||
<h4 className="ng-scope">How would you like to access your
|
||||
wallet?</h4>
|
||||
<label className="radio">
|
||||
<input aria-flowto="aria1"
|
||||
aria-label="Keystore JSON file"
|
||||
type="radio"
|
||||
// ng-model="walletType"
|
||||
value="fileupload"
|
||||
className="ng-pristine ng-untouched ng-valid ng-empty"
|
||||
name="133"/>
|
||||
<span
|
||||
|
||||
// translate=""
|
||||
className="ng-scope">Keystore File (UTC / JSON)</span></label>
|
||||
<label className="radio"><input aria-flowto="aria2" aria-label="private key"
|
||||
type="radio"
|
||||
// ng-model="walletType"
|
||||
value="pasteprivkey"
|
||||
className="ng-pristine ng-untouched ng-valid ng-empty"
|
||||
name="135"/><span
|
||||
// translate=""
|
||||
className="ng-scope">Private Key</span></label>
|
||||
<label className="radio">
|
||||
<input aria-flowto="aria3" aria-label="mnemonic phrase"
|
||||
type="radio"
|
||||
// ng-model="walletType"
|
||||
value="pastemnemonic"
|
||||
className="ng-pristine ng-untouched ng-valid ng-empty"
|
||||
name="137"/><span
|
||||
|
||||
// translate=""
|
||||
className="ng-scope">Mnemonic Phrase</span></label>
|
||||
<label className="radio">
|
||||
<input aria-flowto="aria4" aria-label="parity phrase"
|
||||
type="radio"
|
||||
// ng-model="walletType"
|
||||
value="parityBWallet"
|
||||
className="ng-pristine ng-untouched ng-valid ng-empty"
|
||||
name="139"/><span
|
||||
// translate=""
|
||||
className="ng-scope">Parity Phrase</span></label>
|
||||
<label className="radio"
|
||||
// ng-hide="globalService.currentTab==globalService.tabs.signMsg.id"
|
||||
>
|
||||
<input
|
||||
aria-flowto="aria5" type="radio"
|
||||
aria-label="Ledger Nano S hardware wallet"
|
||||
// ng-model="walletType"
|
||||
|
||||
value="ledger"
|
||||
className="ng-pristine ng-untouched ng-valid ng-empty" name="141"/>Ledger
|
||||
Nano S</label>
|
||||
<label className="radio"
|
||||
// ng-hide="globalService.currentTab==globalService.tabs.signMsg.id"
|
||||
>
|
||||
<input
|
||||
aria-flowto="aria6" type="radio" aria-label="Trezor hardware wallet"
|
||||
// ng-model="walletType"
|
||||
value="trezor"
|
||||
className="ng-pristine ng-untouched ng-valid ng-empty"
|
||||
name="142"/>TREZOR</label>
|
||||
<label className="radio"
|
||||
// ng-hide="globalService.currentTab!==globalService.tabs.viewWalletInfo.id"
|
||||
|
||||
>
|
||||
<input
|
||||
aria-label="address" type="radio"
|
||||
// ng-model="walletType"
|
||||
value="addressOnly"
|
||||
className="ng-pristine ng-untouched ng-valid ng-empty"
|
||||
name="143"/><span>View with Address Only</span></label>
|
||||
</section>
|
||||
</article>
|
||||
</div>
|
||||
</article>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
}
|
26
common/containers/Tabs/ViewWallet/index.jsx
Normal file
26
common/containers/Tabs/ViewWallet/index.jsx
Normal file
@ -0,0 +1,26 @@
|
||||
// @flow
|
||||
import React, { Component } from 'react';
|
||||
import translate from 'translations';
|
||||
|
||||
export default class ViewWallet extends Component {
|
||||
render() {
|
||||
return (
|
||||
<section className="container">
|
||||
<div className="tab-content">
|
||||
<article className="tab-pane active">
|
||||
<article className="collapse-container">
|
||||
<div>
|
||||
<h1>View Wallet Info</h1>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
{translate('VIEWWALLET_Subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
</article>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
}
|
@ -1,2 +1,2 @@
|
||||
export ViewWallet from './ViewWallet'
|
||||
export GenerateWallet from './GenerateWallet'
|
||||
export WalletDecrypt from './WalletDecrypt';
|
||||
export GenerateWallet from './GenerateWallet';
|
||||
|
@ -11,31 +11,33 @@ import { createLogger } from 'redux-logger';
|
||||
import createSagaMiddleware from 'redux-saga';
|
||||
import notificationsSaga from './sagas/notifications';
|
||||
import ensSaga from './sagas/ens';
|
||||
import walletSaga from './sagas/wallet';
|
||||
|
||||
// application styles
|
||||
import 'assets/styles/etherwallet-master.less';
|
||||
|
||||
const sagaMiddleware = createSagaMiddleware();
|
||||
let store;
|
||||
|
||||
const configureStore = () => {
|
||||
let sagaApplied = applyMiddleware(sagaMiddleware);
|
||||
const logger = createLogger({
|
||||
collapsed: true
|
||||
});
|
||||
const sagaMiddleware = createSagaMiddleware();
|
||||
let middleware;
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
window.Perf = Perf;
|
||||
sagaApplied = composeWithDevTools(sagaApplied);
|
||||
const logger = createLogger({
|
||||
collapsed: true
|
||||
});
|
||||
middleware = applyMiddleware(routerMiddleware(history), logger);
|
||||
middleware = composeWithDevTools(
|
||||
applyMiddleware(sagaMiddleware, logger, routerMiddleware(history))
|
||||
);
|
||||
} else {
|
||||
middleware = applyMiddleware(routerMiddleware(history));
|
||||
middleware = applyMiddleware(sagaMiddleware, routerMiddleware(history));
|
||||
}
|
||||
|
||||
store = createStore(RootReducer, sagaApplied, middleware);
|
||||
store = createStore(RootReducer, void 0, middleware);
|
||||
sagaMiddleware.run(notificationsSaga);
|
||||
sagaMiddleware.run(ensSaga);
|
||||
sagaMiddleware.run(walletSaga);
|
||||
return store;
|
||||
};
|
||||
|
||||
|
8
common/libs/nodes/base.js
Normal file
8
common/libs/nodes/base.js
Normal file
@ -0,0 +1,8 @@
|
||||
// @flow
|
||||
|
||||
export default class BaseNode {
|
||||
// FIXME bignumber?
|
||||
queryBalance(address: string): Promise<number> {
|
||||
throw 'Implement me';
|
||||
}
|
||||
}
|
3
common/libs/nodes/index.js
Normal file
3
common/libs/nodes/index.js
Normal file
@ -0,0 +1,3 @@
|
||||
// @flow
|
||||
export { default as BaseNode } from './base';
|
||||
export { default as RPCNode } from './rpc';
|
10
common/libs/nodes/rpc.js
Normal file
10
common/libs/nodes/rpc.js
Normal file
@ -0,0 +1,10 @@
|
||||
// @flow
|
||||
import BaseNode from './base';
|
||||
|
||||
export default class RPCNode extends BaseNode {
|
||||
endpoint: string;
|
||||
constructor(endpoint: string) {
|
||||
super();
|
||||
this.endpoint = endpoint;
|
||||
}
|
||||
}
|
@ -72,3 +72,7 @@ function validateEtherAddress(address: string): boolean {
|
||||
return true;
|
||||
else return isChecksumAddress(address);
|
||||
}
|
||||
|
||||
export function isValidPrivKey(length: number): boolean {
|
||||
return length === 64 || length === 128 || length === 132;
|
||||
}
|
||||
|
7
common/libs/wallet/base.js
Normal file
7
common/libs/wallet/base.js
Normal file
@ -0,0 +1,7 @@
|
||||
// @flow
|
||||
|
||||
export default class BaseWallet {
|
||||
getAddress(): string {
|
||||
throw 'Implement me';
|
||||
}
|
||||
}
|
28
common/libs/wallet/privkey.js
Normal file
28
common/libs/wallet/privkey.js
Normal file
@ -0,0 +1,28 @@
|
||||
// @flow
|
||||
import BaseWallet from './base';
|
||||
import {
|
||||
privateToPublic,
|
||||
publicToAddress,
|
||||
toChecksumAddress
|
||||
} from 'ethereumjs-util';
|
||||
|
||||
export type PrivateKeyUnlockParams = {
|
||||
key: string,
|
||||
password: string
|
||||
};
|
||||
|
||||
export default class PrivKeyWallet extends BaseWallet {
|
||||
privKey: Buffer;
|
||||
pubKey: Buffer;
|
||||
address: Buffer;
|
||||
constructor(params: PrivateKeyUnlockParams) {
|
||||
super();
|
||||
this.privKey = Buffer.from(params.key, 'hex');
|
||||
this.pubKey = privateToPublic(this.privKey);
|
||||
this.address = publicToAddress(this.pubKey);
|
||||
}
|
||||
|
||||
getAddress() {
|
||||
return toChecksumAddress(`0x${this.address.toString('hex')}`);
|
||||
}
|
||||
}
|
@ -1,6 +1,10 @@
|
||||
// @flow
|
||||
import { CONFIG_LANGUAGE_CHANGE, CONFIG_NODE_CHANGE } from 'actions/config';
|
||||
import { languages, nodeList } from '../config/data';
|
||||
import type {
|
||||
ConfigAction,
|
||||
ChangeNodeAction,
|
||||
ChangeLanguageAction
|
||||
} from 'actions/config';
|
||||
import { languages, NODES } from '../config/data';
|
||||
|
||||
export type State = {
|
||||
// FIXME
|
||||
@ -8,25 +12,34 @@ export type State = {
|
||||
nodeSelection: string
|
||||
};
|
||||
|
||||
const initialState = {
|
||||
languageSelection: languages[0],
|
||||
nodeSelection: nodeList[0]
|
||||
const initialState: State = {
|
||||
languageSelection: languages[0].sign,
|
||||
nodeSelection: Object.keys(NODES)[0]
|
||||
};
|
||||
|
||||
export function config(state: State = initialState, action): State {
|
||||
function changeLanguage(state: State, action: ChangeLanguageAction): State {
|
||||
return {
|
||||
...state,
|
||||
languageSelection: action.value
|
||||
};
|
||||
}
|
||||
|
||||
function changeNode(state: State, action: ChangeNodeAction): State {
|
||||
return {
|
||||
...state,
|
||||
nodeSelection: action.value
|
||||
};
|
||||
}
|
||||
|
||||
export function config(
|
||||
state: State = initialState,
|
||||
action: ConfigAction
|
||||
): State {
|
||||
switch (action.type) {
|
||||
case CONFIG_LANGUAGE_CHANGE: {
|
||||
return {
|
||||
...state,
|
||||
languageSelection: action.value
|
||||
};
|
||||
}
|
||||
case CONFIG_NODE_CHANGE: {
|
||||
return {
|
||||
...state,
|
||||
nodeSelection: action.value
|
||||
};
|
||||
}
|
||||
case 'CONFIG_LANGUAGE_CHANGE':
|
||||
return changeLanguage(state, action);
|
||||
case 'CONFIG_NODE_CHANGE':
|
||||
return changeNode(state, action);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
@ -13,15 +13,19 @@ import type { State as NotificationsState } from './notifications';
|
||||
import * as ens from './ens';
|
||||
import type { State as EnsState } from './ens';
|
||||
|
||||
import * as wallet from './wallet';
|
||||
import type { State as WalletState } from './wallet';
|
||||
|
||||
import { reducer as formReducer } from 'redux-form';
|
||||
import { combineReducers } from 'redux';
|
||||
import { routerReducer } from 'react-router-redux';
|
||||
|
||||
export type State = {
|
||||
generateWallet: GenerateWalletState,
|
||||
conig: ConfigState,
|
||||
config: ConfigState,
|
||||
notifications: NotificationsState,
|
||||
ens: EnsState
|
||||
ens: EnsState,
|
||||
wallet: WalletState
|
||||
};
|
||||
|
||||
export default combineReducers({
|
||||
@ -30,6 +34,7 @@ export default combineReducers({
|
||||
...swap,
|
||||
...notifications,
|
||||
...ens,
|
||||
...wallet,
|
||||
form: formReducer,
|
||||
routing: routerReducer
|
||||
});
|
||||
|
43
common/reducers/wallet.js
Normal file
43
common/reducers/wallet.js
Normal file
@ -0,0 +1,43 @@
|
||||
// @flow
|
||||
import type {
|
||||
WalletAction,
|
||||
SaveWalletAction,
|
||||
InitWalletAction
|
||||
} from 'actions/wallet';
|
||||
import BaseWallet from 'libs/wallet/base';
|
||||
|
||||
export type State = {
|
||||
inst: ?BaseWallet,
|
||||
balance: number,
|
||||
tokens: {
|
||||
[string]: number
|
||||
}
|
||||
};
|
||||
|
||||
const initialState: State = {
|
||||
inst: null,
|
||||
balance: 0,
|
||||
tokens: {}
|
||||
};
|
||||
|
||||
function saveWallet(state: State, action: SaveWalletAction): State {
|
||||
return { ...state, inst: action.payload };
|
||||
}
|
||||
|
||||
function initWallet(state: State): State {
|
||||
return { ...state, balance: 0, tokens: {} };
|
||||
}
|
||||
|
||||
export function wallet(
|
||||
state: State = initialState,
|
||||
action: WalletAction
|
||||
): State {
|
||||
switch (action.type) {
|
||||
case 'WALLET_SAVE':
|
||||
return saveWallet(state, action);
|
||||
case 'WALLET_INIT':
|
||||
return initWallet(state);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
@ -1,12 +1,14 @@
|
||||
// @flow
|
||||
import { takeEvery, call, put, select } from 'redux-saga/effects';
|
||||
import { delay } from 'redux-saga';
|
||||
import type { Effect } from 'redux-saga/effects';
|
||||
import { cacheEnsAddress } from 'actions/ens';
|
||||
import type { ResolveEnsNameAction } from 'actions/ens';
|
||||
import { getEnsAddress } from 'selectors/ens';
|
||||
import { donationAddressMap } from 'config/data';
|
||||
|
||||
function* resolveEns(action: ResolveEnsNameAction) {
|
||||
function* resolveEns(action?: ResolveEnsNameAction) {
|
||||
if (!action) return;
|
||||
const ensName = action.payload;
|
||||
// FIXME Add resolve logic
|
||||
//// _ens.getAddress(scope.addressDrtv.ensAddressField, function(data) {
|
||||
@ -29,6 +31,6 @@ function* resolveEns(action: ResolveEnsNameAction) {
|
||||
yield put(cacheEnsAddress(ensName, donationAddressMap.ETH));
|
||||
}
|
||||
|
||||
export default function* notificationsSaga() {
|
||||
export default function* notificationsSaga(): Generator<Effect, void, any> {
|
||||
yield takeEvery('ENS_RESOLVE', resolveEns);
|
||||
}
|
||||
|
@ -1,21 +1,23 @@
|
||||
// @flow
|
||||
import { takeEvery, put, call } from 'redux-saga/effects';
|
||||
import { delay } from 'redux-saga';
|
||||
import type { Effect } from 'redux-saga/effects';
|
||||
import { closeNotification } from 'actions/notifications';
|
||||
import type { ShowNotificationAction } from 'actions/notifications';
|
||||
|
||||
function* handleNotification(action: ShowNotificationAction) {
|
||||
const { duration } = action.payload;
|
||||
// show forever
|
||||
if (duration === 0) {
|
||||
return;
|
||||
}
|
||||
function* handleNotification(action?: ShowNotificationAction) {
|
||||
if (!action) return;
|
||||
const { duration } = action.payload;
|
||||
// show forever
|
||||
if (duration === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME
|
||||
yield call(delay, duration || 5000);
|
||||
yield put(closeNotification(action.payload));
|
||||
// FIXME
|
||||
yield call(delay, duration || 5000);
|
||||
yield put(closeNotification(action.payload));
|
||||
}
|
||||
|
||||
export default function* notificationsSaga() {
|
||||
yield takeEvery('SHOW_NOTIFICATION', handleNotification);
|
||||
export default function* notificationsSaga(): Generator<Effect, void, any> {
|
||||
yield takeEvery('SHOW_NOTIFICATION', handleNotification);
|
||||
}
|
||||
|
26
common/sagas/wallet.js
Normal file
26
common/sagas/wallet.js
Normal file
@ -0,0 +1,26 @@
|
||||
// @flow
|
||||
import { takeEvery, call, put, select } from 'redux-saga/effects';
|
||||
import type { Effect } from 'redux-saga/effects';
|
||||
import { delay } from 'redux-saga';
|
||||
import { saveWallet, initWallet } from 'actions/wallet';
|
||||
import type { UnlockPrivateKeyAction } from 'actions/wallet';
|
||||
import PrivKeyWallet from 'libs/wallet/privkey';
|
||||
|
||||
function* init() {
|
||||
yield put(initWallet());
|
||||
// const node = select(getNode);
|
||||
// yield call();
|
||||
// fetch balance,
|
||||
// fetch tokens
|
||||
yield delay(100);
|
||||
}
|
||||
|
||||
function* unlockPrivateKey(action?: UnlockPrivateKeyAction) {
|
||||
if (!action) return;
|
||||
yield put(saveWallet(new PrivKeyWallet(action.payload)));
|
||||
yield call(init);
|
||||
}
|
||||
|
||||
export default function* notificationsSaga(): Generator<Effect, void, any> {
|
||||
yield takeEvery('WALLET_UNLOCK_PRIVATE_KEY', unlockPrivateKey);
|
||||
}
|
8
common/selectors/config.js
Normal file
8
common/selectors/config.js
Normal file
@ -0,0 +1,8 @@
|
||||
// @flow
|
||||
import type { State } from 'reducers';
|
||||
import { BaseNode } from 'libs/nodes';
|
||||
import { NODES } from 'config/data';
|
||||
|
||||
export function getNodeLib(state: State): BaseNode {
|
||||
return NODES[state.config.nodeSelection].lib;
|
||||
}
|
@ -5,54 +5,56 @@ let fallbackLanguage = 'en';
|
||||
let repository = {};
|
||||
|
||||
const languages = [
|
||||
require('./de'),
|
||||
require('./el'),
|
||||
require('./en'),
|
||||
require('./es'),
|
||||
require('./fi'),
|
||||
require('./fr'),
|
||||
require('./hu'),
|
||||
require('./id'),
|
||||
require('./it'),
|
||||
require('./ja'),
|
||||
require('./nl'),
|
||||
require('./no'),
|
||||
require('./pl'),
|
||||
require('./pt'),
|
||||
require('./ru') /*sk, sl, sv */,
|
||||
require('./ko'),
|
||||
require('./tr'),
|
||||
require('./vi'),
|
||||
require('./zhcn'),
|
||||
require('./zhtw')
|
||||
require('./de'),
|
||||
require('./el'),
|
||||
require('./en'),
|
||||
require('./es'),
|
||||
require('./fi'),
|
||||
require('./fr'),
|
||||
require('./hu'),
|
||||
require('./id'),
|
||||
require('./it'),
|
||||
require('./ja'),
|
||||
require('./nl'),
|
||||
require('./no'),
|
||||
require('./pl'),
|
||||
require('./pt'),
|
||||
require('./ru') /*sk, sl, sv */,
|
||||
require('./ko'),
|
||||
require('./tr'),
|
||||
require('./vi'),
|
||||
require('./zhcn'),
|
||||
require('./zhtw')
|
||||
];
|
||||
|
||||
languages.forEach(l => {
|
||||
repository[l.code] = l.data;
|
||||
repository[l.code] = l.data;
|
||||
});
|
||||
|
||||
export function setLanguage(code: string) {
|
||||
activeLanguage = code;
|
||||
activeLanguage = code;
|
||||
}
|
||||
|
||||
export default function translate(key: string) {
|
||||
return markupToReact(
|
||||
repository[activeLanguage][key] || repository[fallbackLanguage][key] || key
|
||||
);
|
||||
return markupToReact(
|
||||
(repository[activeLanguage] && repository[activeLanguage][key]) ||
|
||||
repository[fallbackLanguage][key] ||
|
||||
key
|
||||
);
|
||||
}
|
||||
|
||||
export function getTranslators() {
|
||||
return [
|
||||
'TranslatorName_1',
|
||||
'TranslatorName_2',
|
||||
'TranslatorName_3',
|
||||
'TranslatorName_4',
|
||||
'TranslatorName_5'
|
||||
].filter(x => {
|
||||
const translated = translate(x);
|
||||
if (typeof translated === 'string') {
|
||||
return !!translated.trim();
|
||||
}
|
||||
return !!translated;
|
||||
});
|
||||
return [
|
||||
'TranslatorName_1',
|
||||
'TranslatorName_2',
|
||||
'TranslatorName_3',
|
||||
'TranslatorName_4',
|
||||
'TranslatorName_5'
|
||||
].filter(x => {
|
||||
const translated = translate(x);
|
||||
if (typeof translated === 'string') {
|
||||
return !!translated.trim();
|
||||
}
|
||||
return !!translated;
|
||||
});
|
||||
}
|
||||
|
566
flow-typed/redux-saga_v0.14.x.js
vendored
Normal file
566
flow-typed/redux-saga_v0.14.x.js
vendored
Normal file
@ -0,0 +1,566 @@
|
||||
declare type ReduxSaga$Predicate<T> = (arg: T) => boolean;
|
||||
|
||||
declare interface ReduxSaga$Task {
|
||||
isRunning(): boolean,
|
||||
isCancelled(): boolean,
|
||||
result(): any,
|
||||
result<T>(): T,
|
||||
error(): any,
|
||||
done: Promise<any>,
|
||||
cancel(): void
|
||||
}
|
||||
|
||||
declare interface ReduxSaga$Buffer<T> {
|
||||
isEmpty(): boolean,
|
||||
put(message: T): void,
|
||||
take(): T
|
||||
}
|
||||
|
||||
declare interface ReduxSaga$Channel<T> {
|
||||
take(cb: (message: T) => void, matcher?: ReduxSaga$Predicate<T>): void,
|
||||
put(message: T): void,
|
||||
close(): void
|
||||
}
|
||||
|
||||
declare module 'redux-saga/effects' {
|
||||
declare type Predicate<T> = ReduxSaga$Predicate<T>;
|
||||
declare type Task = ReduxSaga$Task;
|
||||
declare type Buffer<T> = ReduxSaga$Buffer<T>;
|
||||
declare type Channel<T> = ReduxSaga$Channel<T>;
|
||||
declare type Action = { type: $Subtype<string> };
|
||||
declare type Pattern<T> = string | Predicate<T> | (string | Predicate<T>)[];
|
||||
|
||||
declare type Effect =
|
||||
| TakeEffect<any>
|
||||
| PutEffect<any>
|
||||
| RaceEffect
|
||||
| CallEffect
|
||||
| CpsEffect
|
||||
| ForkEffect
|
||||
| JoinEffect
|
||||
| CancelEffect
|
||||
| SelectEffect
|
||||
| ActionChannelEffect<any>
|
||||
| CancelledEffect
|
||||
| FlushEffect<any>;
|
||||
|
||||
// take
|
||||
declare interface TakeEffectDescriptor<T> {
|
||||
pattern: Pattern<T>,
|
||||
channel: Channel<T>,
|
||||
maybe?: boolean
|
||||
}
|
||||
|
||||
declare interface TakeEffect<T> {
|
||||
TAKE: TakeEffectDescriptor<T>
|
||||
}
|
||||
|
||||
declare var take: {
|
||||
<T>(pattern: Pattern<T>): TakeEffect<T>,
|
||||
<T>(channel: Channel<T>): TakeEffect<T>,
|
||||
maybe: {
|
||||
<T>(pattern: Pattern<T>): TakeEffect<T>,
|
||||
<T>(channel: Channel<T>): TakeEffect<T>
|
||||
}
|
||||
};
|
||||
|
||||
declare var takem: void;
|
||||
|
||||
// put
|
||||
declare interface PutEffectDescriptor<T> {
|
||||
action: T,
|
||||
channel: Channel<T>
|
||||
}
|
||||
|
||||
declare interface PutEffect<T> {
|
||||
PUT: PutEffectDescriptor<T>
|
||||
}
|
||||
|
||||
declare var put: {
|
||||
<T: Action>(action: T): PutEffect<T>,
|
||||
<T: Action>(channel: Channel<T>, action: T): PutEffect<T>,
|
||||
resolve: {
|
||||
<T: Action>(action: T): PutEffect<T>,
|
||||
<T: Action>(channel: Channel<T>, action: T): PutEffect<T>
|
||||
},
|
||||
sync: void
|
||||
};
|
||||
|
||||
// race
|
||||
declare type RaceEffectDescriptor = { [key: string]: Effect };
|
||||
|
||||
declare interface RaceEffect {
|
||||
RACE: RaceEffectDescriptor
|
||||
}
|
||||
|
||||
declare function race(effects: { [key: string]: Effect }): RaceEffect;
|
||||
|
||||
// call & apply
|
||||
declare interface CallEffectDescriptor {
|
||||
context: any,
|
||||
fn: Function,
|
||||
args: any[]
|
||||
}
|
||||
|
||||
declare type Collable0 = () => any;
|
||||
declare type Collable1<A> = (a: A) => any;
|
||||
declare type Collable2<A, B> = (a: A, b: B) => any;
|
||||
declare type Collable3<A, B, C> = (a: A, b: B, c: C) => any;
|
||||
declare type Collable4<A, B, C, D> = (a: A, b: B, c: C, d: D) => any;
|
||||
declare type Collable5<A, B, C, D, E> = (a: A, b: B, c: C, d: D, e: E) => any;
|
||||
declare type CollableR = (...args: mixed[]) => any;
|
||||
|
||||
declare type CallEffectArg<F> = F | [any, F] | { context: any, fn: F };
|
||||
|
||||
declare interface CallEffect {
|
||||
CALL: CallEffectDescriptor
|
||||
}
|
||||
|
||||
declare type CallEffectFactory<R> = {
|
||||
(fn: CallEffectArg<Collable0>): R,
|
||||
<A>(fn: CallEffectArg<Collable1<A>>, a: A): R,
|
||||
<A, B>(fn: CallEffectArg<Collable2<A, B>>, a: A, b: B): R,
|
||||
<A, B, C>(fn: CallEffectArg<Collable3<A, B, C>>, a: A, b: B, c: C): R,
|
||||
<A, B, C, D>(
|
||||
fn: CallEffectArg<Collable4<A, B, C, D>>,
|
||||
a: A,
|
||||
b: B,
|
||||
c: C,
|
||||
d: D
|
||||
): R,
|
||||
<A, B, C, D, E>(
|
||||
fn: CallEffectArg<Collable5<A, B, C, D, E>>,
|
||||
a: A,
|
||||
b: B,
|
||||
c: C,
|
||||
d: D,
|
||||
e: E
|
||||
): R,
|
||||
(fn: CallEffectArg<CollableR>, ...args: any[]): R
|
||||
};
|
||||
|
||||
declare var call: CallEffectFactory<CallEffect>;
|
||||
|
||||
declare var apply: {
|
||||
(context: any, fn: Collable0): CallEffect,
|
||||
<A>(context: any, fn: Collable1<A>, args: [A]): CallEffect,
|
||||
<A, B>(context: any, fn: Collable2<A, B>, args: [A, B]): CallEffect,
|
||||
<A, B, C>(
|
||||
context: any,
|
||||
fn: Collable3<A, B, C>,
|
||||
args: [A, B, C]
|
||||
): CallEffect,
|
||||
<A, B, C, D>(
|
||||
context: any,
|
||||
fn: Collable4<A, B, C, D>,
|
||||
args: [A, B, C, D]
|
||||
): CallEffect,
|
||||
<A, B, C, D, E>(
|
||||
context: any,
|
||||
fn: Collable5<A, B, C, D, E>,
|
||||
args: [A, B, C, D, E]
|
||||
): CallEffect,
|
||||
(context: any, fn: CollableR, args: any[]): CallEffect
|
||||
};
|
||||
|
||||
// cps
|
||||
declare interface CpsEffect {
|
||||
CPS: CallEffectDescriptor
|
||||
}
|
||||
|
||||
declare type CpsCallback = (error: any, result: any) => void;
|
||||
|
||||
declare var cps: {
|
||||
(fn: CallEffectArg<Collable1<CpsCallback>>): CpsEffect,
|
||||
<A>(fn: CallEffectArg<Collable2<A, CpsCallback>>, a: A): CpsEffect,
|
||||
<A, B>(
|
||||
fn: CallEffectArg<Collable3<A, B, CpsCallback>>,
|
||||
a: A,
|
||||
b: B
|
||||
): CpsEffect,
|
||||
<A, B, C>(
|
||||
fn: CallEffectArg<Collable4<A, B, C, CpsCallback>>,
|
||||
a: A,
|
||||
b: B,
|
||||
c: C
|
||||
): CpsEffect,
|
||||
<A, B, C, D>(
|
||||
fn: CallEffectArg<Collable5<A, B, C, D, CpsCallback>>,
|
||||
a: A,
|
||||
b: B,
|
||||
c: C,
|
||||
d: D
|
||||
): CpsEffect
|
||||
};
|
||||
|
||||
// fork & spawn
|
||||
declare interface ForkEffectDescriptor extends CallEffectDescriptor {
|
||||
detached?: boolean
|
||||
}
|
||||
|
||||
declare interface ForkEffect {
|
||||
FORK: ForkEffectDescriptor
|
||||
}
|
||||
|
||||
declare var fork: CallEffectFactory<ForkEffect>;
|
||||
declare var spawn: CallEffectFactory<ForkEffect>;
|
||||
|
||||
// join
|
||||
declare interface JoinEffect {
|
||||
JOIN: Task
|
||||
}
|
||||
|
||||
declare function join(task: Task): JoinEffect;
|
||||
|
||||
// cancel
|
||||
declare interface CancelEffect {
|
||||
CANCEL: Task
|
||||
}
|
||||
|
||||
declare function cancel(task: Task): CancelEffect;
|
||||
|
||||
// select
|
||||
declare interface SelectEffectDescriptor {
|
||||
selector(state: any, ...args: any[]): any,
|
||||
args: any[]
|
||||
}
|
||||
|
||||
declare interface SelectEffect {
|
||||
SELECT: SelectEffectDescriptor
|
||||
}
|
||||
|
||||
declare var select: {
|
||||
(): SelectEffect,
|
||||
<S>(selector: Collable1<S>): SelectEffect,
|
||||
<S, A>(selector: Collable2<S, A>, a: A): SelectEffect,
|
||||
<S, A, B>(selector: Collable3<S, A, B>, a: A, b: B): SelectEffect,
|
||||
<S, A, B, C>(
|
||||
selector: Collable4<S, A, B, C>,
|
||||
a: A,
|
||||
b: B,
|
||||
c: C
|
||||
): SelectEffect,
|
||||
<S, A, B, C, D>(
|
||||
selector: Collable5<S, A, B, C, D>,
|
||||
a: A,
|
||||
b: B,
|
||||
c: C,
|
||||
d: D
|
||||
): SelectEffect,
|
||||
(selector: CollableR, ...rest: any[]): SelectEffect
|
||||
};
|
||||
|
||||
// actionChannel
|
||||
declare interface ActionChannelEffectDescriptor<T> {
|
||||
pattern: Pattern<T>,
|
||||
buffer: Buffer<T>
|
||||
}
|
||||
|
||||
declare interface ActionChannelEffect<T> {
|
||||
ACTION_CHANNEL: ActionChannelEffectDescriptor<T>
|
||||
}
|
||||
|
||||
declare function actionChannel<T>(
|
||||
pattern: Pattern<T>,
|
||||
buffer?: Buffer<T>
|
||||
): ActionChannelEffect<T>;
|
||||
|
||||
// actionChannel
|
||||
declare interface CancelledEffect {
|
||||
CANCELLED: {}
|
||||
}
|
||||
|
||||
declare function cancelled(): CancelledEffect;
|
||||
|
||||
// flush
|
||||
declare interface FlushEffect<T> {
|
||||
FLUSH: Channel<T>
|
||||
}
|
||||
|
||||
declare function flush<T>(channel: Channel<T>): FlushEffect<T>;
|
||||
|
||||
// takeEvery & takeLatest
|
||||
declare type Workable0<A> = (action?: A) => any;
|
||||
declare type Workable1<A, B> = (b: B, action?: A) => any;
|
||||
declare type Workable2<A, B, C> = (b: B, c: C, action?: A) => any;
|
||||
declare type Workable3<A, B, C, D> = (b: B, c: C, d: D, action?: A) => any;
|
||||
declare type Workable4<A, B, C, D, E> = (
|
||||
b: B,
|
||||
c: C,
|
||||
d: D,
|
||||
e: E,
|
||||
action?: A
|
||||
) => any;
|
||||
declare type WorkableR<A, B, C, D, E, F> = (
|
||||
b: B,
|
||||
c: C,
|
||||
d: D,
|
||||
e: E,
|
||||
f: F,
|
||||
...args: mixed[]
|
||||
) => any;
|
||||
|
||||
declare interface TakeHelper {
|
||||
<A>(pattern: Pattern<A>, worker: Workable0<A>): ForkEffect,
|
||||
<A, B>(pattern: Pattern<A>, worker: Workable1<A, B>, b: B): ForkEffect,
|
||||
<A, B, C>(
|
||||
pattern: Pattern<A>,
|
||||
worker: Workable2<A, B, C>,
|
||||
b: B,
|
||||
c: C
|
||||
): ForkEffect,
|
||||
<A, B, C, D>(
|
||||
pattern: Pattern<A>,
|
||||
worker: Workable3<A, B, C, D>,
|
||||
b: B,
|
||||
c: C,
|
||||
d: D
|
||||
): ForkEffect,
|
||||
<A, B, C, D, E>(
|
||||
pattern: Pattern<A>,
|
||||
worker: Workable4<A, B, C, D, E>,
|
||||
b: B,
|
||||
c: C,
|
||||
d: D,
|
||||
e: E
|
||||
): ForkEffect,
|
||||
<A, B, C, D, E, F>(
|
||||
pattern: Pattern<A>,
|
||||
worker: WorkableR<A, B, C, D, E, F>,
|
||||
b: B,
|
||||
c: C,
|
||||
d: D,
|
||||
e: E,
|
||||
f: F,
|
||||
...rest: any[]
|
||||
): ForkEffect
|
||||
}
|
||||
|
||||
declare var takeEvery: TakeHelper;
|
||||
declare var takeLatest: TakeHelper;
|
||||
|
||||
// throttle
|
||||
declare var throttle: {
|
||||
<A>(ms: number, pattern: Pattern<A>, worker: Workable0<A>): ForkEffect,
|
||||
<A, B>(
|
||||
ms: number,
|
||||
pattern: Pattern<A>,
|
||||
worker: Workable1<A, B>,
|
||||
b: B
|
||||
): ForkEffect,
|
||||
<A, B, C>(
|
||||
ms: number,
|
||||
pattern: Pattern<A>,
|
||||
worker: Workable2<A, B, C>,
|
||||
b: B,
|
||||
c: C
|
||||
): ForkEffect,
|
||||
<A, B, C, D>(
|
||||
ms: number,
|
||||
pattern: Pattern<A>,
|
||||
worker: Workable3<A, B, C, D>,
|
||||
b: B,
|
||||
c: C,
|
||||
d: D
|
||||
): ForkEffect,
|
||||
<A, B, C, D, E>(
|
||||
ms: number,
|
||||
pattern: Pattern<A>,
|
||||
worker: Workable4<A, B, C, D, E>,
|
||||
b: B,
|
||||
c: C,
|
||||
d: D,
|
||||
e: E
|
||||
): ForkEffect,
|
||||
<A, B, C, D, E, F>(
|
||||
ms: number,
|
||||
pattern: Pattern<A>,
|
||||
worker: WorkableR<A, B, C, D, E, F>,
|
||||
b: B,
|
||||
c: C,
|
||||
d: D,
|
||||
e: E,
|
||||
f: F,
|
||||
...rest: any[]
|
||||
): ForkEffect
|
||||
};
|
||||
}
|
||||
|
||||
declare module 'redux-saga/utils' {
|
||||
import type {
|
||||
Effect,
|
||||
TakeEffectDescriptor,
|
||||
PutEffectDescriptor,
|
||||
RaceEffectDescriptor,
|
||||
CallEffectDescriptor,
|
||||
ForkEffectDescriptor,
|
||||
SelectEffectDescriptor,
|
||||
ActionChannelEffectDescriptor
|
||||
} from 'redux-saga/effects';
|
||||
|
||||
declare type Task = ReduxSaga$Task;
|
||||
declare type Channel<T> = ReduxSaga$Channel<T>;
|
||||
declare type Is = ReduxSaga$Predicate<*>;
|
||||
declare interface Deferred<R> {
|
||||
resolve(result: R): void,
|
||||
reject(error: any): void,
|
||||
promise: Promise<R>
|
||||
}
|
||||
declare interface MockTask extends Task {
|
||||
setRunning(running: boolean): void,
|
||||
setResult(result: any): void,
|
||||
setError(error: any): void
|
||||
}
|
||||
|
||||
declare var TASK: '@@redux-saga/TASK';
|
||||
|
||||
declare var SAGA_ACTION: '@@redux-saga/SAGA_ACTION';
|
||||
|
||||
declare function noop(): void;
|
||||
|
||||
declare var is: {
|
||||
undef: Is,
|
||||
notUndef: Is,
|
||||
func: Is,
|
||||
number: Is,
|
||||
array: Is,
|
||||
promise: Is,
|
||||
iterator: Is,
|
||||
task: Is,
|
||||
observable: Is,
|
||||
buffer: Is,
|
||||
pattern: Is,
|
||||
channel: Is,
|
||||
helper: Is,
|
||||
stringableFunc: Is
|
||||
};
|
||||
|
||||
declare function deferred<T, R>(props?: T): T & Deferred<R>;
|
||||
|
||||
declare function arrayOfDeffered<T>(length: number): Deferred<T>[];
|
||||
|
||||
declare function createMockTask(): MockTask;
|
||||
|
||||
declare var asEffect: {
|
||||
take<T>(effect: Effect): ?TakeEffectDescriptor<T>,
|
||||
put<T>(effect: Effect): ?PutEffectDescriptor<T>,
|
||||
race(effect: Effect): ?RaceEffectDescriptor,
|
||||
call(effect: Effect): ?CallEffectDescriptor,
|
||||
cps(effect: Effect): ?CallEffectDescriptor,
|
||||
fork(effect: Effect): ?ForkEffectDescriptor,
|
||||
join(effect: Effect): ?Task,
|
||||
cancel(effect: Effect): ?Task,
|
||||
select(effect: Effect): ?SelectEffectDescriptor,
|
||||
actionChannel<T>(effect: Effect): ?ActionChannelEffectDescriptor<T>,
|
||||
cancelled(effect: Effect): ?{},
|
||||
flush<T>(effect: Effect): ?Channel<T>
|
||||
};
|
||||
|
||||
declare var CHANNEL_END: {
|
||||
toString(): '@@redux-saga/CHANNEL_END'
|
||||
};
|
||||
}
|
||||
|
||||
declare module 'redux-saga' {
|
||||
import type { Middleware } from 'redux';
|
||||
import type { Effect } from 'redux-saga/effects';
|
||||
import typeof * as Effects from 'redux-saga/effects';
|
||||
import typeof * as Utils from 'redux-saga/utils';
|
||||
|
||||
declare export type Predicate<T> = ReduxSaga$Predicate<T>;
|
||||
declare export type Task = ReduxSaga$Task;
|
||||
declare export type Buffer<T> = ReduxSaga$Buffer<T>;
|
||||
declare export type Channel<T> = ReduxSaga$Channel<T>;
|
||||
|
||||
declare export interface SagaMonitor {
|
||||
effectTriggered(options: {
|
||||
effectId: number,
|
||||
parentEffectId: number,
|
||||
label: string,
|
||||
root?: boolean,
|
||||
effect: Effect
|
||||
}): void,
|
||||
effectResolved(effectId: number, result: any): void,
|
||||
effectRejected(effectId: number, err: any): void,
|
||||
effectCancelled(effectId: number): void,
|
||||
actionDispatched<A>(action: A): void
|
||||
}
|
||||
|
||||
declare type Saga0 = () => Generator<*, *, *>;
|
||||
declare type Saga1<A> = (a: A) => Generator<*, *, *>;
|
||||
declare type Saga2<A, B> = (a: A, b: B) => Generator<*, *, *>;
|
||||
declare type Saga3<A, B, C> = (a: A, b: B, c: C) => Generator<*, *, *>;
|
||||
declare type Saga4<A, B, C, D> = (
|
||||
a: A,
|
||||
b: B,
|
||||
c: C,
|
||||
d: D
|
||||
) => Generator<*, *, *>;
|
||||
declare type SagaR = (...args: mixed[]) => Generator<*, *, *>;
|
||||
|
||||
declare export type SagaMiddleware<S, A> = Middleware<S, A> & {
|
||||
run(saga: Saga0): Task,
|
||||
run<A>(saga: Saga1<A>, a: A): Task,
|
||||
run<A, B>(saga: Saga2<A, B>, a: A, B: B): Task,
|
||||
run<A, B, C>(saga: Saga3<A, B, C>, a: A, B: B, c: C): Task,
|
||||
run<A, B, C, T4>(saga: Saga4<A, B, C, T4>, a: A, B: B, c: C, d: T4): Task,
|
||||
run(saga: SagaR, ...args: any[]): Task
|
||||
};
|
||||
|
||||
declare export type Emit<T> = (input: T) => void;
|
||||
|
||||
declare export default function createSagaMiddleware<T>(options?: {
|
||||
sagaMonitor?: SagaMonitor,
|
||||
emitter: (emit: Emit<T>) => Emit<T>
|
||||
}): SagaMiddleware<*, *>;
|
||||
|
||||
declare export type Unsubscribe = () => void;
|
||||
declare export type Subscribe<T> = (cb: (input: T) => void) => Unsubscribe;
|
||||
declare export type Logger = (
|
||||
level: 'info' | 'warning' | 'error',
|
||||
...args: Array<any>
|
||||
) => void;
|
||||
|
||||
declare export function runSaga<S, SA, DA>(
|
||||
saga: Generator<*, *, *>,
|
||||
io: {
|
||||
subscribe?: Subscribe<SA>,
|
||||
dispatch?: (input: DA) => any,
|
||||
getState?: () => S,
|
||||
sagaMonitor?: SagaMonitor,
|
||||
logger?: Logger,
|
||||
onError?: void
|
||||
}
|
||||
): Task;
|
||||
|
||||
declare export var END: { type: '@@redux-saga/CHANNEL_END' };
|
||||
|
||||
declare export function eventChannel<T>(
|
||||
subscribe: Subscribe<T>,
|
||||
buffer?: Buffer<T>,
|
||||
matcher?: Predicate<T>
|
||||
): Channel<T>;
|
||||
|
||||
declare export function channel<T>(buffer?: Buffer<T>): Channel<T>;
|
||||
|
||||
declare export var buffers: {
|
||||
none<T>(): Buffer<T>,
|
||||
fixed<T>(limit?: number): Buffer<T>,
|
||||
dropping<T>(limit?: number): Buffer<T>,
|
||||
sliding<T>(limit?: number): Buffer<T>,
|
||||
expanding<T>(limit?: number): Buffer<T>
|
||||
};
|
||||
|
||||
// deprecate
|
||||
declare export var takeEvery: void;
|
||||
declare export var takeLatest: void;
|
||||
declare export var throttle: void;
|
||||
|
||||
declare export function delay(ms: number, rest: void): Promise<boolean>;
|
||||
declare export function delay<T>(ms: number, val: T): Promise<T>;
|
||||
|
||||
declare export var CANCEL: '@@redux-saga/cancelPromise';
|
||||
|
||||
declare export var effects: Effects;
|
||||
declare export var utils: Utils;
|
||||
}
|
@ -10,6 +10,7 @@
|
||||
"^routing": "<rootDir>/common/routing",
|
||||
"^components$": "<rootDir>/common/components",
|
||||
"^containers$": "<rootDir>/common/containers",
|
||||
"^translations(.*)": "<rootDir>/common/translations$1"
|
||||
"^translations(.*)": "<rootDir>/common/translations$1",
|
||||
"^libs(.*)": "<rootDir>/common/libs$1"
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
"ethereumjs-util": "^5.1.2",
|
||||
"idna-uts46": "^1.1.0",
|
||||
"lodash": "^4.17.4",
|
||||
"ethereumjs-wallet": "^0.6.0",
|
||||
"prop-types": "^15.5.8",
|
||||
"react": "^15.4.2",
|
||||
"react-dom": "^15.4.2",
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { CHANGE_LANGUAGE, CONFIG_LANGUAGE_CHANGE } from '../../common/actions/config';
|
||||
import { changeLanguage } from '../../common/actions/config';
|
||||
|
||||
describe('actions', () => {
|
||||
it('should create an action to change language to index', () => {
|
||||
const value = { name: 'English', sign: 'en' };
|
||||
const expectedAction = {
|
||||
type: CONFIG_LANGUAGE_CHANGE,
|
||||
value
|
||||
};
|
||||
expect(CHANGE_LANGUAGE(value)).toEqual(expectedAction);
|
||||
});
|
||||
it('should create an action to change language to index', () => {
|
||||
const value = 'en';
|
||||
const expectedAction = {
|
||||
type: 'CONFIG_LANGUAGE_CHANGE',
|
||||
value
|
||||
};
|
||||
expect(changeLanguage(value)).toEqual(expectedAction);
|
||||
});
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user