summaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
authorHaishan <[email protected]>2018-10-26 01:02:59 +0800
committerHaishan <[email protected]>2018-10-27 14:28:25 +0800
commitabfab4f1adb40e3463fa9e90aff3e92bfdf03693 (patch)
tree46347cc195d59a2b0622752c71742b82476cce69 /src/components
parent4e7efe2accc67a63f9e3bc4ed4365acefb4fb883 (diff)
ui(proxy): new UI for proxy page
Diffstat (limited to 'src/components')
-rw-r--r--src/components/APIConfig.js23
-rw-r--r--src/components/APIConfig.module.scss4
-rw-r--r--src/components/Proxies.js73
-rw-r--r--src/components/Proxies.module.scss40
-rw-r--r--src/components/Proxy.js117
-rw-r--r--src/components/Proxy.module.scss32
-rw-r--r--src/components/ProxyGroup.js80
-rw-r--r--src/components/ProxyGroup.module.scss20
-rw-r--r--src/components/ProxyLatency.js44
-rw-r--r--src/components/ProxyLatency.module.scss8
-rw-r--r--src/components/StyleGuide.js11
11 files changed, 271 insertions, 181 deletions
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 (
- <div className={s0.root}>
+ <div
+ className={s0.root}
+ ref={e => (this.content = e)}
+ tabIndex="1"
+ onKeyDown={this.handleContentOnKeyDown}
+ >
<div className={s0.header}>RESTful API config for Clash</div>
<div className={s0.body}>
<div className={s0.group}>
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 (
<div>
<ContentHeader title="Proxies" />
-
- <div className={s0.root}>
+ <div>
<div className={s0.btnGroup}>
<Button label="Test Latency" onClick={requestDelayAll} />
</div>
- <div className={th}>
- <div className={s0.col1}>Name</div>
- <div className={s0.col2}>Type</div>
- <div className={s0.col3}>All</div>
- </div>
-
- <div>
- {Object.keys(proxies).map(k => {
- const o = proxies[k];
- return <ProxyRow name={k} key={k} {...o} />;
- })}
- </div>
+ {groupNames.map(groupName => {
+ return (
+ <div className={s0.group} key={groupName}>
+ <ProxyGroup name={groupName} />
+ </div>
+ );
+ })}
</div>
</div>
);
}
}
-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 (
- <div className={s0.row}>
- <div className={s0.col1}>{name}</div>
- <div className={s0.col2}>{type}</div>
- {all ? (
- <div className={s0.col3 + ' border-left'}>
- {all.map(p => {
- return (
- <div className={s0.colItem} key={p}>
- <Proxy name={p} parentName={name} checked={p === now} />
- </div>
- );
- })}
- </div>
- ) : null}
- </div>
- );
- }
-}
-
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 (
- <label className={s0.Proxy} htmlFor={id}>
- <input
- type="radio"
- id={id}
- checked={checked}
- value={name}
- onChange={this.handleRadioOnChange}
- />
- <div className={s0.name}>{name}</div>
- <LatencyLabel val={latency} />
- </label>
- );
- }
-}
+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 (
- <div className={s0.LatencyLabel} style={style}>
- <div>{val}</div>
- <div>ms</div>
+ <div className={s0.proxy}>
+ <div className={s0.left} style={{ color }}>
+ <Icon id={iconId} width={80} height={80} />
+ </div>
+ <div className={s0.right}>
+ <div className={s0.proxyName}>{name}</div>
+ {latency ? <ProxyLatency latency={latency} /> : null}
+ </div>
</div>
);
}
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 (
+ <div className={s0.group}>
+ <div className={s0.header}>
+ <h2>
+ <span>{name}</span>
+ <span>{group.type}</span>
+ </h2>
+ </div>
+ <div className={s0.list}>
+ {list.map(proxyName => {
+ return (
+ <div
+ className={s0.proxy}
+ key={proxyName}
+ onClick={() => switchProxy(name, proxyName)}
+ >
+ <Proxy name={proxyName} now={proxyName === group.now} />
+ </div>
+ );
+ })}
+ </div>
+ </div>
+ );
+ }
+}
+
+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 (
+ <span className={s0.proxyLatency} style={{ color: bg }}>
+ {error !== '' ? <span>{error}</span> : <span>{number} ms</span>}
+ </span>
+ );
+ }
+}
+
+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'
@@ -37,6 +38,9 @@ class StyleGuide extends PureComponent {
return (
<div>
<Pane>
+ <Proxy2 />
+ </Pane>
+ <Pane>
<Switch />
</Pane>
<Pane>
@@ -53,9 +57,6 @@ class StyleGuide extends PureComponent {
<Pane>
<Button label="Test Latency" />
</Pane>
- <Modal isOpen={true} onRequestClose={() => {}}>
- <APIConfig />
- </Modal>
</div>
);
}