From abfab4f1adb40e3463fa9e90aff3e92bfdf03693 Mon Sep 17 00:00:00 2001 From: Haishan Date: Fri, 26 Oct 2018 01:02:59 +0800 Subject: ui(proxy): new UI for proxy page --- src/components/APIConfig.js | 23 ++++++- src/components/APIConfig.module.scss | 4 +- src/components/Proxies.js | 73 ++++++-------------- src/components/Proxies.module.scss | 40 +---------- src/components/Proxy.js | 117 +++++++++++++------------------- src/components/Proxy.module.scss | 32 +++++---- src/components/ProxyGroup.js | 80 ++++++++++++++++++++++ src/components/ProxyGroup.module.scss | 20 ++++++ src/components/ProxyLatency.js | 44 ++++++++++++ src/components/ProxyLatency.module.scss | 8 +++ src/components/StyleGuide.js | 11 +-- 11 files changed, 271 insertions(+), 181 deletions(-) create mode 100644 src/components/ProxyGroup.js create mode 100644 src/components/ProxyGroup.module.scss create mode 100644 src/components/ProxyLatency.js create mode 100644 src/components/ProxyLatency.module.scss (limited to 'src/components') diff --git a/src/components/APIConfig.js b/src/components/APIConfig.js index ce2b7fe..4e85a69 100644 --- a/src/components/APIConfig.js +++ b/src/components/APIConfig.js @@ -33,6 +33,10 @@ class APIConfig extends Component { secret: this.props.apiConfig.secret }; + componentDidMount() { + this.content.focus(); + } + handleInputOnChange = e => { const target = e.target; const { name } = target; @@ -46,15 +50,30 @@ class APIConfig extends Component { this.setState({ [name]: value }); }; - handleConfirmOnClick = () => { + updateClashAPIConfig() { const { hostname, port, secret } = this.state; this.props.updateClashAPIConfig({ hostname, port, secret }); + } + + handleConfirmOnClick = () => { + this.updateClashAPIConfig(); + }; + + handleContentOnKeyDown = e => { + // enter keyCode is 13 + if (e.keyCode !== 13) return; + this.updateClashAPIConfig(); }; render() { const { hostname, port, secret } = this.state; return ( -
+
(this.content = e)} + tabIndex="1" + onKeyDown={this.handleContentOnKeyDown} + >
RESTful API config for Clash
diff --git a/src/components/APIConfig.module.scss b/src/components/APIConfig.module.scss index d723016..0edf1b6 100644 --- a/src/components/APIConfig.module.scss +++ b/src/components/APIConfig.module.scss @@ -1,5 +1,7 @@ .root { - // + &:focus { + outline: none; + } } .header { diff --git a/src/components/Proxies.js b/src/components/Proxies.js index f6d7caf..98876ae 100644 --- a/src/components/Proxies.js +++ b/src/components/Proxies.js @@ -2,22 +2,24 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import ContentHeader from 'c/ContentHeader'; -import Proxy from 'c/Proxy'; +import ProxyGroup from 'c/ProxyGroup'; import Button from 'c/Button'; -import cx from 'classnames'; import s0 from 'c/Proxies.module.scss'; -const th = cx(s0.row, s0.th, 'border-bottom'); -// const colItem = cx(s0.colItem, 'border-bottom'); - import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; -import { getUserProxies, fetchProxies, requestDelayAll } from 'd/proxies'; +import { + getUserProxies, + getProxyGroupNames, + fetchProxies, + requestDelayAll +} from 'd/proxies'; function mapStateToProps(s) { return { - proxies: getUserProxies(s) + proxies: getUserProxies(s), + groupNames: getProxyGroupNames(s) }; } @@ -30,7 +32,7 @@ function mapDispatchToProps(dispatch) { class Proxies extends Component { static propTypes = { - proxies: PropTypes.object.isRequired, + groupNames: PropTypes.array.isRequired, fetchProxies: PropTypes.func.isRequired, requestDelayAll: PropTypes.func.isRequired }; @@ -40,64 +42,27 @@ class Proxies extends Component { } render() { - const { proxies, requestDelayAll } = this.props; - + const { groupNames, requestDelayAll } = this.props; return (
- -
+
-
-
Name
-
Type
-
All
-
- -
- {Object.keys(proxies).map(k => { - const o = proxies[k]; - return ; - })} -
+ {groupNames.map(groupName => { + return ( +
+ +
+ ); + })}
); } } -class ProxyRow extends Component { - static propTypes = { - name: PropTypes.string.isRequired, - type: PropTypes.string.isRequired, - all: PropTypes.array, - now: PropTypes.string - }; - - render() { - const { name, type, all, now } = this.props; - return ( -
-
{name}
-
{type}
- {all ? ( -
- {all.map(p => { - return ( -
- -
- ); - })} -
- ) : null} -
- ); - } -} - export default connect( mapStateToProps, mapDispatchToProps diff --git a/src/components/Proxies.module.scss b/src/components/Proxies.module.scss index 3b11056..9a73ee6 100644 --- a/src/components/Proxies.module.scss +++ b/src/components/Proxies.module.scss @@ -1,47 +1,13 @@ -$heightHeader: 76px; - .root { - color: #eee; - padding: 10px 40px; - height: calc(100vh - #{$heightHeader}); - overflow: scroll; } -.row { - display: flex; -} - -.th { - font-weight: bold; -} - -.col1 { - width: 150px; - padding: 8px; - display: flex; - align-items: center; -} - -.col2 { - width: 150px; - padding: 8px; - display: flex; - align-items: center; -} - -.col3 { - width: 350px; - margin: 16px 8px; - padding-left: 20px; -} - -.colItem { - padding: 6px; +.group { + padding: 10px 40px; } .btnGroup { color: #eee; - // padding: 10px 40px; + padding: 0 40px; display: flex; justify-content: flex-end; } diff --git a/src/components/Proxy.js b/src/components/Proxy.js index b3d071b..2bb9435 100644 --- a/src/components/Proxy.js +++ b/src/components/Proxy.js @@ -1,92 +1,71 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import s0 from 'c/Proxy.module.scss'; +import Icon from 'c/Icon'; +import ProxyLatency from 'c/ProxyLatency'; -import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; -import { getDelay, switchProxy, requestDelayForProxy } from 'd/proxies'; +import globe from 's/globe.svg'; +import ss from 's/ss.svg'; +import vmess from 's/vmess.svg'; +import auto from 's/auto.svg'; +import fallback from 's/fallback.svg'; -const mapStateToProps = state => { - const delay = getDelay(state); - return { delay }; -}; +import s0 from './Proxy.module.scss'; -const mapDispatchToProps = dispatch => { - return { - switchProxy: bindActionCreators(switchProxy, dispatch), - requestDelay: bindActionCreators(requestDelayForProxy, dispatch) - }; +import { connect } from 'react-redux'; +import { getDelay, getUserProxies } from 'd/proxies'; + +const colors = { + Vmess: '#ca3487', + Shadowsocks: '#1a7dc0', + Socks5: '#2a477a', + URLTest: '#3483e8', + Fallback: '#3483e8' }; -const colorMap = { - good: '#67C23A', - normal: '#E6A23C', - bad: '#F56C6C', - na: '#909399' +const icons = { + Vmess: vmess.id, + Shadowsocks: ss.id, + Socks5: globe.id, + URLTest: auto.id, + Fallback: fallback.id }; -class Proxy extends Component { - static propTypes = { - name: PropTypes.string.isRequired, - parentName: PropTypes.string, - checked: PropTypes.bool, - switchProxy: PropTypes.func, - requestDelay: PropTypes.func, - delay: PropTypes.object - }; +// typeof Proxy = 'Shadowsocks' | 'Vmess' | 'Socks5'; - handleRadioOnChange = () => { - const { name, parentName, checked, switchProxy } = this.props; - if (checked) return; - switchProxy(parentName, name); +const mapStateToProps = s => { + return { + proxies: getUserProxies(s), + delay: getDelay(s) }; +}; - render() { - const { name, parentName, checked, delay } = this.props; - const id = parentName + ':' + name; - const latency = delay[name] || 0; - return ( - - ); - } -} +const mapDispatchToProps = null; -class LatencyLabel extends Component { +class Proxy extends Component { static propTypes = { - val: PropTypes.number + now: PropTypes.bool, + delay: PropTypes.object, + proxies: PropTypes.object, + name: PropTypes.string }; render() { - const { val } = this.props; - let bg = colorMap.na; + const { name, proxies, delay, now } = this.props; + const latency = delay[name]; + const proxy = proxies[name]; + const color = now ? colors[proxy.type] : '#555'; + const iconId = icons[proxy.type]; - if (val < 100) { - bg = colorMap.good; - } else if (val < 300) { - bg = colorMap.normal; - } else { - bg = colorMap.bad; - } - const style = { background: bg }; - if (val === 0 || !val) { - style.opacity = '0'; - style.visibility = 'hidden'; - } return ( -
-
{val}
-
ms
+
+
+ +
+
+
{name}
+ {latency ? : null} +
); } diff --git a/src/components/Proxy.module.scss b/src/components/Proxy.module.scss index 735c348..20cb494 100644 --- a/src/components/Proxy.module.scss +++ b/src/components/Proxy.module.scss @@ -1,22 +1,28 @@ -.Proxy { +.proxy { display: flex; - align-items: center; + cursor: pointer; - .name { - flex: 1; - // width: 100px; - padding-left: 10px; + svg { + transition: transform 0.4s ease, color 0.4s ease; + } + &:hover { + svg { + transform: scale(1.1); + color: #aaa; + } } } -.LatencyLabel { +.left { display: flex; align-items: center; - padding: 5px; - border-radius: 5px; - background: #e6a23c; + justify-content: center; +} - div:nth-child(2) { - padding-left: 4px; - } +.right { + padding-left: 20px; +} + +.proxyName { + margin: 10px 0; } diff --git a/src/components/ProxyGroup.js b/src/components/ProxyGroup.js new file mode 100644 index 0000000..b697b27 --- /dev/null +++ b/src/components/ProxyGroup.js @@ -0,0 +1,80 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import memoize from 'memoize-one'; + +import Proxy from 'c/Proxy'; + +import s0 from './ProxyGroup.module.scss'; + +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import { getUserProxies, switchProxy } from 'd/proxies'; + +const mapStateToProps = s => { + return { + proxies: getUserProxies(s) + }; +}; + +const mapDispatchToProps = dispatch => { + return { + switchProxy: bindActionCreators(switchProxy, dispatch) + }; +}; + +// should move this to sth like constants.js +// const userProxyTypes = ['Shadowsocks', 'Vmess', 'Socks5']; + +class ProxyGroup extends Component { + static propTypes = { + // group name + name: PropTypes.string.isRequired, + proxies: PropTypes.object, + switchProxy: PropTypes.func + }; + + reOrderProxies = memoize((list, now) => { + const a = [now]; + list.forEach(i => i !== now && a.push(i)); + return a; + }); + + render() { + const { name, proxies, switchProxy } = this.props; + const group = proxies[name]; + let list; + if (group.all) { + list = this.reOrderProxies(group.all, group.now); + } else { + list = [group.now]; + } + return ( +
+
+

+ {name} + {group.type} +

+
+
+ {list.map(proxyName => { + return ( +
switchProxy(name, proxyName)} + > + +
+ ); + })} +
+
+ ); + } +} + +export default connect( + mapStateToProps, + mapDispatchToProps +)(ProxyGroup); diff --git a/src/components/ProxyGroup.module.scss b/src/components/ProxyGroup.module.scss new file mode 100644 index 0000000..43cbdaa --- /dev/null +++ b/src/components/ProxyGroup.module.scss @@ -0,0 +1,20 @@ +.header { + h2 { + span:nth-child(2) { + font-size: 12px; + color: #777; + font-weight: normal; + margin: 0 0.3em; + } + } +} + +.list { + display: flex; + flex-wrap: wrap; +} + +.proxy { + width: 300px; + padding: 10px 5px; +} diff --git a/src/components/ProxyLatency.js b/src/components/ProxyLatency.js new file mode 100644 index 0000000..5d1aaee --- /dev/null +++ b/src/components/ProxyLatency.js @@ -0,0 +1,44 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; + +import s0 from './ProxyLatency.module.scss'; + +const colorMap = { + good: '#67C23A', + normal: '#E6A23C', + bad: '#F56C6C', + na: '#909399' +}; + +class C extends Component { + static propTypes = { + latency: PropTypes.shape({ + number: PropTypes.number, + error: PropTypes.string + }) + }; + + render() { + const { latency } = this.props; + const { number, error } = latency; + let bg; + + if (error !== '') { + bg = colorMap.na; + } else if (number < 200) { + bg = colorMap.good; + } else if (number < 400) { + bg = colorMap.normal; + } else { + bg = colorMap.bad; + } + + return ( + + {error !== '' ? {error} : {number} ms} + + ); + } +} + +export default C; diff --git a/src/components/ProxyLatency.module.scss b/src/components/ProxyLatency.module.scss new file mode 100644 index 0000000..61485bd --- /dev/null +++ b/src/components/ProxyLatency.module.scss @@ -0,0 +1,8 @@ +.proxyLatency { + border-radius: 20px; + margin: 10px 0; + // padding: 3px 5px; + padding: 3px 0; + color: #eee; + // background: #ccc; +} diff --git a/src/components/StyleGuide.js b/src/components/StyleGuide.js index 3fe142d..0bb3813 100644 --- a/src/components/StyleGuide.js +++ b/src/components/StyleGuide.js @@ -5,8 +5,9 @@ import ToggleSwitch from 'c/ToggleSwitch'; import Input from 'c/Input'; import Switch from 'c/Switch'; import Button from 'c/Button'; -import Modal from 'c/Modal'; -import APIConfig from 'c/APIConfig'; +// import Modal from 'c/Modal'; +// import APIConfig from 'c/APIConfig'; +import Proxy2 from 'c/Proxy2'; const paneStyle = { padding: '20px 0' @@ -36,6 +37,9 @@ class StyleGuide extends PureComponent { render() { return (
+ + + @@ -53,9 +57,6 @@ class StyleGuide extends PureComponent {
); } -- cgit v1.3.1