type dropdown, config actions

This commit is contained in:
crptm 2017-07-04 05:25:01 +04:00
parent 13bb96e966
commit 85759d73b4
14 changed files with 256 additions and 278 deletions

View File

@ -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);
export type ChangeNodeAction = {
type: 'CONFIG_NODE_CHANGE',
// FIXME $keyof?
value: string
};
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
};
type: 'CONFIG_LANGUAGE_CHANGE',
value: sign
};
}
export const CHANGE_NODE = (value: any) =>
Object({
type: CONFIG_NODE_CHANGE,
export function changeNode(value: string): ChangeNodeAction {
return {
type: 'CONFIG_NODE_CHANGE',
value
});
};
}

View File

@ -1,26 +1,23 @@
// @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>
@ -41,10 +38,10 @@ export default class Header extends Component {
</span>
&nbsp;&nbsp;&nbsp;
<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'}>
@ -53,23 +50,23 @@ export default class Header extends Component {
</a>
</li>
]}
onChange={changeLanguage}
onChange={this.changeLanguage}
/>
&nbsp;&nbsp;&nbsp;
<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}
@ -79,8 +76,11 @@ export default class Header extends Component {
</section>
<TabsOptions {...this.props} />
</div>
);
}
changeLanguage = (value: { sign: string }) => {
this.props.changeLanguage(value.sign);
};
}

View File

@ -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';

View File

@ -1,26 +1,21 @@
// @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
};
// FIXME
props: {
value: any,
options: any[],
type Props<T> = {
value: T,
options: T[],
ariaLabel: string,
formatTitle: (option: any) => any,
formatTitle: (option: T) => any,
extra?: any,
onChange: (value: any) => void
};
onChange: (value: T) => void
}
type State = {
expanded: boolean
}
export default class DropdownComponent<T: *> extends Component<void, Props<T>, State> {
props: Props<T>
state = {
expanded: false

View File

@ -1,3 +1,6 @@
// @flow
import { RPCNode } from 'libs/nodes';
export const DONATION_ADDRESSES_MAP = {
BTC: '1MEWT2SGbqtz6mPCgFcnea8XmWV5Z4Wc6',
ETH: '0x7cB57B5A97eAbe94205C07890BE4c1aD31E486A8',
@ -99,70 +102,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'),
}
};

View File

@ -2,32 +2,23 @@
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,
// BEGIN ACTUAL
languageSelection: PropTypes.object,
changeLanguage: PropTypes.func,
changeNode: PropTypes.func,
nodeSelection: PropTypes.object,
showNotification: PropTypes.func
changeLanguage: typeof actions.changeLanguage,
changeNode: typeof actions.changeNode,
handleWindowResize: () => void
};
render() {
@ -72,16 +63,4 @@ function mapStateToProps(state) {
};
}
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);

View File

@ -0,0 +1,8 @@
// @flow
export default class BaseNode {
// FIXME bignumber?
queryBalance(address: string): Promise<number> {
throw 'Implement me';
}
}

View 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
View 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;
}
}

View File

@ -1,36 +1,39 @@
// @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
languageSelection: string,
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 {
switch (action.type) {
case CONFIG_LANGUAGE_CHANGE: {
function changeLanguage(state: State, action: ChangeLanguageAction): State {
return {
...state,
languageSelection: action.value
};
}
}
case CONFIG_NODE_CHANGE: {
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 changeLanguage(state, action);
case 'CONFIG_NODE_CHANGE':
return changeNode(state, action);
default:
return state
return state;
}
}

View File

@ -22,7 +22,7 @@ import { routerReducer } from 'react-router-redux';
export type State = {
generateWallet: GenerateWalletState,
conig: ConfigState,
config: ConfigState,
notifications: NotificationsState,
ens: EnsState,
wallet: WalletState

View File

@ -8,7 +8,7 @@ import PrivKeyWallet from 'libs/wallet/privkey';
function* init() {
yield put(initWallet());
// const node = select(node);
// const node = select(getNode);
// yield call();
// fetch balance,
// fetch tokens

View 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;
}

View File

@ -37,7 +37,9 @@ export function setLanguage(code: string) {
export default function translate(key: string) {
return markupToReact(
repository[activeLanguage][key] || repository[fallbackLanguage][key] || key
(repository[activeLanguage] && repository[activeLanguage][key]) ||
repository[fallbackLanguage][key] ||
key
);
}