anyproxy
Version:
A fully configurable HTTP/HTTPS proxy in Node.js
313 lines (270 loc) • 9.21 kB
JSX
import React, { PropTypes } from 'react';
import ReactDOM from 'react-dom';
import { createStore, applyMiddleware } from 'redux';
import { Provider, connect } from 'react-redux';
import { LocaleProvider } from 'antd';
import enUS from 'antd/lib/locale-provider/en_US';
import createSagaMiddleware from 'redux-saga';
import rootSaga from 'saga/rootSaga';
import { MenuKeyMap } from 'common/constant';
import { getQueryParameter } from 'common/commonUtil';
import reducer from 'reducer/rootReducer';
import HeaderMenu from 'component/header-menu';
import RecordPanel from 'component/record-panel';
import RecordFilter from 'component/record-filter';
import MapLocal from 'component/map-local';
import WsListener from 'component/ws-listener';
import RecordDetail from 'component/record-detail';
import LeftMenu from 'component/left-menu';
import DownloadRootCA from 'component/download-root-ca';
require('./style/antd-reset.global.less');
import Style from './index.less';
import CommonStyle from './style/common.less';
const {
RECORD_FILTER: RECORD_FILTER_MENU_KEY,
MAP_LOCAL: MAP_LOCAL_MENU_KEY,
ROOT_CA: ROOT_CA_MENU_KEY
} = MenuKeyMap;
const sagaMiddleware = createSagaMiddleware();
const store = createStore(reducer, applyMiddleware(sagaMiddleware));
sagaMiddleware.run(rootSaga);
class App extends React.Component {
constructor() {
super();
this.state = {
showResizePanel: false,
panelIndex: '',
inAppMode: getQueryParameter('in_app_mode'),
refreshing: true
};
this.onResizePanelClose = this.onResizePanelClose.bind(this);
this.onRecordScroll = this.onRecordScroll.bind(this);
this.stopRefresh = this.stopRefresh.bind(this);
this.resumeFresh = this.resumeFresh.bind(this);
this.detectIfToStopRefreshing = this.detectIfToStopRefreshing.bind(this);
this.scrollHandler = this.scrollHandler.bind(this);
this.initRecrodPanelWrapperRef = this.initRecrodPanelWrapperRef.bind(this);
this.recordTableRef = null;
this.wsListenerRef = null;
this.lastScrollTop = 0;
this.scrollHandlerTimeout = null;
this.stopRefreshTimout = null;
this.stopRefreshTokenScrollTop = null; // the token used to decide the move distance
}
static propTypes = {
dispatch: PropTypes.func,
requestRecord: PropTypes.object,
globalStatus: PropTypes.object
}
stopRefresh() {
this.wsListenerRef && this.wsListenerRef.stopPanelRefreshing();
this.state.refreshing = false;
this.setState({
refreshing: false
});
}
resumeFresh() {
this.wsListenerRef && this.wsListenerRef.resumePanelRefreshing();
this.state.refreshing = true;
this.setState({
refreshing: true
});
}
onResizePanelClose() {
this.setState({
showResizePanel: false
});
}
// if is scrolling up during refresh, will stop the refresh
detectIfToStopRefreshing(currentScrollTop) {
if (!this.stopRefreshTokenScrollTop) {
this.stopRefreshTokenScrollTop = currentScrollTop;
}
this.stopRefreshTimout = setTimeout(() => {
// if the scrollbar is scrolled up more than 50px, stop refreshing
if ((this.stopRefreshTokenScrollTop - currentScrollTop) > 50) {
this.stopRefresh();
this.stopRefreshTokenScrollTop = null;
}
}, 50);
}
initRecrodPanelWrapperRef(ref) {
this.recordTableRef = ref;
ref.addEventListener('wheel', this.onRecordScroll, { passive: true });
}
scrollHandler() {
if (!this.recordTableRef || !this.wsListenerRef) {
return;
}
const self = this;
const scrollTop = this.recordTableRef.scrollTop;
if (scrollTop < this.lastScrollTop || (this.lastScrollTop === 0)) {
this.detectIfToStopRefreshing(scrollTop);
// load more previous record when scrolled to top
if (scrollTop < 10) {
self.state.loadingPrev = true;
self.setState({
loadingPrev: true
});
//TODO: hide the loading stauts after 1000 ms, a lazy way to hide it when there is no previous records
setTimeout(() => {
self.state.loadingPrev = false;
self.setState({
loadingPrev: false
});
}, 1000);
this.wsListenerRef.loadPrevious();
}
} else if (scrollTop >= this.lastScrollTop) {
const recordPanelHeight = this.recordTableRef.firstChild.clientHeight;
const tableHeight = this.recordTableRef.clientHeight;
// when close to bottom in less than 30, load more next records
if (scrollTop + tableHeight + 30 > recordPanelHeight) {
this.state.loadNext = true;
this.setState({
loadingNext: true
});
this.wsListenerRef.loadNext();
}
}
this.lastScrollTop = scrollTop;
}
onRecordScroll() {
this.scrollHandlerTimeout && clearTimeout(this.scrollHandlerTimeout);
this.scrollHandlerTimeout = setTimeout(() => {
this.scrollHandler();
}, 60);
}
getResumeFreshDiv() {
if (!this.props.globalStatus.showNewRecordTip) {
return null;
}
return (
<div className={Style.resumeTip} onClick={this.resumeFresh} >
<div className={CommonStyle.relativeWrapper}>
<span>New Records Detected.</span>
<span className={Style.arrowDown} />
</div>
</div>
);
}
getMiddlePanel() {
const { activeMenuKey } = this.props.globalStatus;
let middlePanel = null;
// TODO: move the logic of resizepanel out to here, keep each panel component independant
switch (activeMenuKey) {
case RECORD_FILTER_MENU_KEY: {
middlePanel = <RecordFilter />;
break;
}
case MAP_LOCAL_MENU_KEY: {
middlePanel = <MapLocal />;
break;
}
case ROOT_CA_MENU_KEY: {
middlePanel = <DownloadRootCA />;
break;
}
default: {
middlePanel = null;
break;
}
}
return middlePanel;
}
componentWillReceiveProps(nextProps) {
const { recordList: nextRecordList } = nextProps.requestRecord;
const { recordList: currentRecordList } = this.props.requestRecord;
// if there are new data, reset the status of loadingNext and loadingPrev
if (nextRecordList !== currentRecordList) {
// scroll the window to last remembered position, when in loading pre mode
if (this.state.loadingPrev) {
const nextBeginId = nextRecordList[0].id;
const currentBeginId = currentRecordList[0].id;
if (nextBeginId < currentBeginId) {
// each line is limited to 29px
const scrollPosition = 29 * (nextRecordList.length - currentRecordList.length);
if (this.recordTableRef) {
setTimeout(() => {
this.recordTableRef.scrollTop = scrollPosition;
}, 200);
}
}
this.state.loadingPrev = false;
}
this.setState({
loadingNext: false,
loadingPrev: false
});
}
}
componentDidUpdate() {
if (this.state.refreshing && this.recordTableRef && !this.state.loadingPrev) {
this.recordTableRef.scrollTop = this.recordTableRef.scrollHeight;
}
}
componentDidMount() {
if (this.state.refreshing && this.recordTableRef) {
this.recordTableRef.scrollTop = this.recordTableRef.scrollHeight;
}
}
render() {
const { lastActiveRecordId, currentActiveRecordId } = this.props.globalStatus;
const leftMenuDiv = (
<div className={Style.leftPanel} >
<LeftMenu />
</div>
);
return (
<div className={Style.indexWrapper} >
{this.state.inAppMode ? null : leftMenuDiv}
<div className={Style.middlePanel} >
{this.getMiddlePanel()}
</div>
<div className={Style.rightPanel} >
<div className={Style.headerWrapper} >
<HeaderMenu resumeRefreshFunc={this.resumeFresh} />
</div>
<div
className={Style.tableWrapper}
ref={this.initRecrodPanelWrapperRef}
>
<RecordPanel
data={this.props.requestRecord.recordList}
lastActiveRecordId={lastActiveRecordId}
currentActiveRecordId={currentActiveRecordId}
dispatch={this.props.dispatch}
loadingNext={this.state.loadingNext}
loadingPrev={this.state.loadingPrev}
stopRefresh={this.stopRefresh}
/>
</div>
{this.getResumeFreshDiv()}
</div>
<WsListener
ref={(ref) => { this.wsListenerRef = ref; }}
dispatch={this.props.dispatch}
globalStatus={this.props.globalStatus}
/>
<RecordDetail
globalStatus={this.props.globalStatus}
requestRecord={this.props.requestRecord}
dispatch={this.props.dispatch}
/>
</div>
);
}
}
function select(state) {
return {
requestRecord: state.requestRecord,
globalStatus: state.globalStatus
};
}
const ReduxApp = connect(select)(App);
ReactDOM.render(
<LocaleProvider locale={enUS} >
<Provider store={store} >
<ReduxApp />
</Provider>
</LocaleProvider>, document.getElementById('root'));