cleanup header

This commit is contained in:
crptm 2017-05-24 03:06:01 +04:00
parent 345eedcd53
commit 94b7111761
10 changed files with 337 additions and 419 deletions

10
.editorconfig Normal file
View File

@ -0,0 +1,10 @@
# http://editorconfig.org
root = true
[{*.js,*.jsx}]
end_of_line = lf
charset = utf-8
indent_size = 4
indent_style = space
trim_trailing_whitespace = true
insert_final_newline = true

View File

@ -1,22 +1,12 @@
// @flow
export const CONFIG_LANGUAGE_CHANGE = 'CONFIG_LANGUAGE_CHANGE';
export const CONFIG_LANGUAGE_DROPDOWN_TOGGLE = 'CONFIG_LANGUAGE_DROPDOWN_TOGGLE';
export const CONFIG_NODE_CHANGE = 'CONFIG_NODE_CHANGE';
export const CONFIG_NODE_DROPDOWN_TOGGLE = 'CONFIG_NODE_DROPDOWN_TOGGLE';
export const CHANGE_LANGUAGE = (index: number) => Object({
type: CONFIG_LANGUAGE_CHANGE, index: index
export const CHANGE_LANGUAGE = (value: any) => Object({
type: CONFIG_LANGUAGE_CHANGE, value
})
export const TOGGLE_LANGUAGE_DROPDOWN = () => Object({
type: CONFIG_LANGUAGE_DROPDOWN_TOGGLE
})
export const CHANGE_NODE = (index: number) => Object({
type: CONFIG_NODE_CHANGE, index: index
})
export const TOGGLE_NODE_DROPDOWN = () => Object({
type: CONFIG_NODE_DROPDOWN_TOGGLE
export const CHANGE_NODE = (value: any) => Object({
type: CONFIG_NODE_CHANGE, value
})

View File

@ -1,61 +0,0 @@
import React, {Component} from "react";
import {languages} from "reducers/config";
import PropTypes from "prop-types";
export default class LanguageDropdownComponent extends Component {
constructor(props) {
super(props);
}
static propTypes = {
changeLanguage: PropTypes.func,
languageSelection: PropTypes.number,
languageToggle: PropTypes.bool,
toggleLanguageDropdown: PropTypes.func
};
render() {
let {
languageSelection,
changeLanguage,
toggleLanguageDropdown,
languageToggle
} = this.props;
return (
<span className="dropdown">
<a tabIndex="0" aria-haspopup="true" aria-expanded="false"
aria-label="change language. current language English"
className="dropdown-toggle"
onClick={() => toggleLanguageDropdown()}>
{languages[languageSelection].name}
<i className="caret"/>
</a>
{
languageToggle &&
<ul className="dropdown-menu">
{
languages.map((object, i) => {
return (
<li key={i}>
<a className={i === languageSelection ? 'active' : ''}
onClick={() => changeLanguage(i)}>
{object.name}
</a>
</li>
)
})
}
<li role="separator" className="divider"/>
<li>
<a data-toggle="modal" data-target="#disclaimerModal">
Disclaimer
</a>
</li>
</ul>
}
</span>
)
}
}

View File

@ -1,65 +0,0 @@
import React, {Component} from "react";
import {nodeList} from "reducers/config";
import PropTypes from "prop-types";
export default class NodeDropdownComponent extends Component {
constructor(props) {
super(props);
}
static propTypes = {
changeNode: PropTypes.func,
toggleNodeDropdown: PropTypes.func,
nodeSelection: PropTypes.number,
nodeToggle: PropTypes.bool
};
customNodeModalOpen() {
}
render() {
let {
changeNode,
toggleNodeDropdown,
nodeSelection,
nodeToggle
} = this.props;
return (
<span className="dropdown">
<a aria-haspopup="true"
aria-label="change node. current node ETH node by MyEtherWallet"
className="dropdown-toggle"
onClick={() => toggleNodeDropdown()}>
{nodeList[nodeSelection].name}
<small>{' '} ({nodeList[nodeSelection].service})</small>
<i className="caret"/>
</a>
{
nodeToggle &&
<ul className="dropdown-menu">
{nodeList.map((object, i) => {
return (
<li key={i} onClick={() => changeNode(i)}>
<a>
{object.name}
<small className={i === nodeSelection ? 'active' : ''}>
{' '}({object.service})
</small>
</a>
</li>
)
})}
<li>
<a onClick={() => this.customNodeModalOpen()}>
Add Custom Node
</a>
</li>
</ul>
}
</span>
)
}
}

View File

@ -1,55 +1,24 @@
import React, {Component} from "react";
import NodeDropdownComponent from "./components/NodeDropdownComponent";
import LanguageDropDownComponent from "./components/LanguageDropdownComponent";
import PropTypes from "prop-types";
import TabsOptions from "./components/TabsOptions";
import {Link} from "react-router";
// @flow
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import TabsOptions from './components/TabsOptions';
import { Link } from 'react-router';
import Dropdown from '../ui/Dropdown';
import { languages, nodeList } from '../../config/data';
export default class Header extends Component {
constructor(props) {
super(props)
}
static propTypes = {
// LanguageDropDownComponentProps
// Language DropDown
changeLanguage: PropTypes.func,
toggleLanguageDropdown: PropTypes.func,
languageSelection: PropTypes.number,
languageToggle: PropTypes.bool,
languageSelection: PropTypes.object,
// NodeDropdownComponentProps
// Node Dropdown
changeNode: PropTypes.func,
toggleNodeDropdown: PropTypes.func,
nodeSelection: PropTypes.number,
nodeToggle: PropTypes.bool
nodeSelection: PropTypes.object
};
render() {
let {
languageSelection,
changeLanguage,
toggleLanguageDropdown,
languageToggle,
changeNode,
toggleNodeDropdown,
nodeSelection,
nodeToggle
} = this.props;
let LanguageDropDownComponentProps = {
languageSelection,
changeLanguage,
toggleLanguageDropdown,
languageToggle
}
let NodeDropdownComponentProps = {
changeNode,
toggleNodeDropdown,
nodeSelection,
nodeToggle
}
const { languageSelection, changeLanguage, changeNode, nodeSelection } = this.props;
return (
<div>
@ -58,19 +27,51 @@ export default class Header extends Component {
<Link to={'/'} className="brand" aria-label="Go to homepage">
{/* TODO - don't hardcode image path*/}
<img
src={"https://www.myetherwallet.com/images/logo-myetherwallet.svg"}
src={'https://www.myetherwallet.com/images/logo-myetherwallet.svg'}
height="64px"
width="245px"
alt="MyEtherWallet"/>
alt="MyEtherWallet"
/>
</Link>
<div className="tagline">
<span style={{ maxWidth: '395px' }}>
Open-Source &amp; Client-Side Ether Wallet · v3.6.0
Open-Source & Client-Side Ether Wallet · v3.6.0
</span>
&nbsp;&nbsp;&nbsp;
<LanguageDropDownComponent {...LanguageDropDownComponentProps}/>
<Dropdown
ariaLabel={`change language. current language ${languageSelection.name}`}
options={languages}
formatTitle={o => o.name}
value={languageSelection}
extra={[
<li key={'separator'} role="separator" className="divider" />,
<li key={'disclaimer'}>
<a data-toggle="modal" data-target="#disclaimerModal">
Disclaimer
</a>
</li>
]}
onChange={changeLanguage}
/>
&nbsp;&nbsp;&nbsp;
<NodeDropdownComponent {...NodeDropdownComponentProps}/>
<Dropdown
ariaLabel={`change node. current node ${nodeSelection.name} node by ${nodeSelection.service}`}
options={nodeList}
formatTitle={o => [
o.name,
' ',
<small key="service">({o.service})</small>
]}
value={nodeSelection}
extra={
<li>
<a onClick={() => {}}>
Add Custom Node
</a>
</li>
}
onChange={changeNode}
/>
</div>
</section>
</section>
@ -78,6 +79,6 @@ export default class Header extends Component {
<TabsOptions {...{}} />
</div>
)
);
}
}

View File

@ -0,0 +1,81 @@
// @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[],
ariaLabel: string,
formatTitle: (option: any) => any,
extra?: any,
onChange: () => void
};
state = {
expanded: false
};
render() {
const { options, value, ariaLabel, extra } = this.props;
return (
<span className="dropdown">
<a
tabIndex="0"
aria-haspopup="true"
aria-expanded="false"
aria-label={ariaLabel}
className="dropdown-toggle"
onClick={this.toggleExpanded}
>
{this.formatTitle(value)}
<i className="caret" />
</a>
{this.state.expanded &&
<ul className="dropdown-menu">
{options.map((option, i) => {
return (
<li key={i}>
<a
className={option === value ? 'active' : ''}
onClick={this.onChange.bind(null, option)}
>
{this.formatTitle(option)}
</a>
</li>
);
})}
{extra}
</ul>}
</span>
);
}
formatTitle(option: any) {
return this.props.formatTitle(option);
}
toggleExpanded = () => {
this.setState(state => {
return {
expanded: !state.expanded
};
});
};
onChange = (value: any) => {
this.props.onChange(value);
this.setState({ expanded: false });
};
}

View File

@ -1,4 +1,2 @@
'use strict';
// Settings configured here will be merged into the final config object.
export default {}

159
common/config/data.js Normal file
View File

@ -0,0 +1,159 @@
export const languages = [
{
sign: 'en',
name: 'English'
},
{
sign: 'de',
name: 'Deutsch'
},
{
sign: 'el',
name: 'Ελληνικά'
},
{
sign: 'es',
name: 'Español'
},
{
sign: 'fi',
name: 'Suomi'
},
{
sign: 'fr',
name: 'Français'
},
{
sign: 'hu',
name: 'Magyar'
},
{
sign: 'id',
name: 'Indonesian'
},
{
sign: 'it',
name: 'Italiano'
},
{
sign: 'ja',
name: '日本語'
},
{
sign: 'nl',
name: 'Nederlands'
},
{
sign: 'no',
name: 'Norsk Bokmål'
},
{
sign: 'pl',
name: 'Polski'
},
{
sign: 'pt',
name: 'Português'
},
{
sign: 'ru',
name: 'Русский'
},
// {
// 'sign': 'sk',
// 'name': 'Slovenčina'
// },
// {
// 'sign': 'sl',
// 'name': 'Slovenščina'
// },
// {
// 'sign': 'sv',
// 'name': 'Svenska'
// },
{
sign: 'tr',
name: 'Türkçe'
},
{
sign: 'vi',
name: 'Tiếng Việt'
},
{
sign: 'zhcn',
name: '简体中文'
},
{
sign: 'zhtw',
name: '繁體中文'
}
];
export const nodeList = [
{
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', '')
}
]

View File

@ -1,14 +1,13 @@
import React, {Component} from "react";
import {connect} from "react-redux";
import {Footer, Header} from "components";
import PropTypes from "prop-types";
import {CHANGE_LANGUAGE, CHANGE_NODE, TOGGLE_LANGUAGE_DROPDOWN, TOGGLE_NODE_DROPDOWN} from "actions/config";
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Footer, Header } from 'components';
import PropTypes from 'prop-types';
import { CHANGE_LANGUAGE, CHANGE_NODE } from 'actions/config';
class App extends Component {
constructor(props) {
super(props)
super(props);
}
static propTypes = {
@ -20,51 +19,37 @@ class App extends Component {
isMobile: PropTypes.bool,
// BEGIN ACTUAL
languageSelection: PropTypes.number,
languageToggle: PropTypes.bool,
languageSelection: PropTypes.object,
changeLanguage: PropTypes.func,
toggleLanguageDropdown: PropTypes.func,
changeNode: PropTypes.func,
toggleNodeDropdown: PropTypes.func,
nodeSelection: PropTypes.number,
nodeToggle: PropTypes.bool,
}
nodeSelection: PropTypes.object
};
componentWillMount() {
let {handleWindowResize} = this.props
window.addEventListener('resize', handleWindowResize)
let { handleWindowResize } = this.props;
window.addEventListener('resize', handleWindowResize);
}
render() {
let {
children,
// APP
languageSelection,
changeLanguage,
languageToggle,
toggleLanguageDropdown,
changeNode,
toggleNodeDropdown,
nodeSelection,
nodeToggle
nodeSelection
} = this.props;
// let title = children.props.route.name;
let headerProps = {
changeLanguage,
toggleLanguageDropdown,
languageSelection,
languageToggle,
changeNode,
toggleNodeDropdown,
nodeSelection,
nodeToggle
}
nodeSelection
};
return (
<div className="page-layout">
@ -77,7 +62,7 @@ class App extends Component {
</main>
</div>
)
);
}
}
@ -87,24 +72,18 @@ function mapStateToProps(state) {
nodeToggle: state.config.nodeToggle,
languageSelection: state.config.languageSelection,
languageToggle: state.config.languageToggle
}
};
}
function mapDispatchToProps(dispatch) {
return {
changeNode: (i: number) => {
dispatch(CHANGE_NODE(i))
},
toggleNodeDropdown: () => {
dispatch(TOGGLE_NODE_DROPDOWN())
dispatch(CHANGE_NODE(i));
},
changeLanguage: (i: number) => {
dispatch(CHANGE_LANGUAGE(i))
},
toggleLanguageDropdown: () => {
dispatch(TOGGLE_LANGUAGE_DROPDOWN())
}
dispatch(CHANGE_LANGUAGE(i));
}
};
}
export default connect(mapStateToProps, mapDispatchToProps)(App)
export default connect(mapStateToProps, mapDispatchToProps)(App);

View File

@ -1,16 +1,12 @@
import {
CONFIG_LANGUAGE_CHANGE,
CONFIG_LANGUAGE_DROPDOWN_TOGGLE,
CONFIG_NODE_CHANGE,
CONFIG_NODE_DROPDOWN_TOGGLE
} from 'actions/config'
CONFIG_NODE_CHANGE
} from 'actions/config';
import {languages, nodeList} from '../config/data';
const initialState = {
languageSelection: 0,
languageToggle: false,
nodeSelection: 0,
nodeToggle: false
languageSelection: languages[0],
nodeSelection: nodeList[0]
}
export function config(state = initialState, action) {
@ -18,186 +14,16 @@ export function config(state = initialState, action) {
case CONFIG_LANGUAGE_CHANGE: {
return {
...state,
languageSelection: action.index,
languageToggle: false
}
}
case CONFIG_LANGUAGE_DROPDOWN_TOGGLE: {
return {
...state,
languageToggle: !state.languageToggle
languageSelection: action.value
}
}
case CONFIG_NODE_CHANGE: {
return {
...state,
nodeSelection: action.index,
nodeToggle: false
}
}
case CONFIG_NODE_DROPDOWN_TOGGLE: {
return {
...state,
nodeToggle: !state.nodeToggle
nodeSelection: action.value
}
}
default:
return state
}
}
export const languages = [
{
'sign': 'en',
'name': 'English'
},
{
'sign': 'de',
'name': 'Deutsch'
},
{
'sign': 'el',
'name': 'Ελληνικά'
},
{
'sign': 'es',
'name': 'Español'
},
{
'sign': 'fi',
'name': 'Suomi'
},
{
'sign': 'fr',
'name': 'Français'
},
{
'sign': 'hu',
'name': 'Magyar'
},
{
'sign': 'id',
'name': 'Indonesian'
},
{
'sign': 'it',
'name': 'Italiano'
},
{
'sign': 'ja',
'name': '日本語'
}, {
'sign': 'nl',
'name': 'Nederlands'
}, {
'sign': 'no',
'name': 'Norsk Bokmål'
}, {
'sign': 'pl',
'name': 'Polski'
}, {
'sign': 'pt',
'name': 'Português'
},
{
'sign': 'ru',
'name': 'Русский'
},
// {
// 'sign': 'sk',
// 'name': 'Slovenčina'
// },
// {
// 'sign': 'sl',
// 'name': 'Slovenščina'
// },
// {
// 'sign': 'sv',
// 'name': 'Svenska'
// },
{
'sign': 'tr',
'name': 'Türkçe'
},
{
'sign': 'vi',
'name': 'Tiếng Việt'
},
{
'sign': 'zhcn',
'name': '简体中文'
},
{
'sign': 'zhtw',
'name': '繁體中文'
}
];
export const nodeList = [
{
'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', '')
}
]