[web] rewrite ProxyApp and MainView with es6
This commit is contained in:
parent
7707d096d2
commit
851bb4bf68
|
@ -0,0 +1,5 @@
|
||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
|
@ -4,30 +4,38 @@ import {applyMiddleware, createStore} from 'redux'
|
||||||
import { Provider } from 'react-redux'
|
import { Provider } from 'react-redux'
|
||||||
import createLogger from 'redux-logger'
|
import createLogger from 'redux-logger'
|
||||||
import thunkMiddleware from 'redux-thunk'
|
import thunkMiddleware from 'redux-thunk'
|
||||||
|
import { Route, Router as ReactRouter, hashHistory, Redirect } from "react-router"
|
||||||
|
|
||||||
import Connection from "./connection"
|
import Connection from "./connection"
|
||||||
import {App} from "./components/proxyapp.js"
|
import ProxyApp from "./components/ProxyApp"
|
||||||
import rootReducer from './ducks/index';
|
import MainView from './components/MainView'
|
||||||
import {addLogEntry} from "./ducks/eventLog";
|
import rootReducer from './ducks/index'
|
||||||
|
import { addLogEntry } from "./ducks/eventLog"
|
||||||
|
|
||||||
// logger must be last
|
// logger must be last
|
||||||
const logger = createLogger();
|
|
||||||
const store = createStore(
|
const store = createStore(
|
||||||
rootReducer,
|
rootReducer,
|
||||||
applyMiddleware(thunkMiddleware, logger)
|
applyMiddleware(thunkMiddleware, createLogger())
|
||||||
);
|
)
|
||||||
|
|
||||||
window.onerror = function (msg) {
|
window.addEventListener('error', msg => {
|
||||||
store.dispatch(addLogEntry(msg));
|
store.dispatch(addLogEntry(msg))
|
||||||
};
|
})
|
||||||
|
|
||||||
|
// @todo remove this
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
window.ws = new Connection("/updates", store.dispatch);
|
window.ws = new Connection("/updates", store.dispatch)
|
||||||
|
|
||||||
render(
|
render(
|
||||||
<Provider store={store}>{App}</Provider>,
|
<Provider store={store}>
|
||||||
|
<ReactRouter history={hashHistory}>
|
||||||
|
<Redirect from="/" to="/flows" />
|
||||||
|
<Route path="/" component={ProxyApp}>
|
||||||
|
<Route path="flows" component={MainView}/>
|
||||||
|
<Route path="flows/:flowId/:detailTab" component={MainView}/>
|
||||||
|
</Route>
|
||||||
|
</ReactRouter>
|
||||||
|
</Provider>,
|
||||||
document.getElementById("mitmproxy")
|
document.getElementById("mitmproxy")
|
||||||
);
|
)
|
||||||
|
})
|
||||||
});
|
|
||||||
|
|
|
@ -0,0 +1,191 @@
|
||||||
|
import React, { Component } from "react"
|
||||||
|
|
||||||
|
import { FlowActions } from "../actions.js"
|
||||||
|
import { Query } from "../actions.js"
|
||||||
|
import { Key } from "../utils.js"
|
||||||
|
import { Splitter } from "./common.js"
|
||||||
|
import FlowTable from "./flowtable.js"
|
||||||
|
import FlowView from "./flowview/index.js"
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
import { selectFlow, setFilter, setHighlight } from "../ducks/flows"
|
||||||
|
|
||||||
|
class MainView extends Component {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo move to actions
|
||||||
|
* @todo replace with mapStateToProps
|
||||||
|
*/
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
// Update redux store with route changes
|
||||||
|
if (nextProps.routeParams.flowId !== (nextProps.selectedFlow || {}).id) {
|
||||||
|
this.props.selectFlow(nextProps.routeParams.flowId)
|
||||||
|
}
|
||||||
|
if (nextProps.location.query[Query.SEARCH] !== nextProps.filter) {
|
||||||
|
this.props.setFilter(nextProps.location.query[Query.SEARCH], false)
|
||||||
|
}
|
||||||
|
if (nextProps.location.query[Query.HIGHLIGHT] !== nextProps.highlight) {
|
||||||
|
this.props.setHighlight(nextProps.location.query[Query.HIGHLIGHT], false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo move to actions
|
||||||
|
*/
|
||||||
|
selectFlow(flow) {
|
||||||
|
if (flow) {
|
||||||
|
this.props.updateLocation(`/flows/${flow.id}/${this.props.routeParams.detailTab || "request"}`)
|
||||||
|
} else {
|
||||||
|
this.props.updateLocation("/flows")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo move to actions
|
||||||
|
*/
|
||||||
|
selectFlowRelative(shift) {
|
||||||
|
const { flows, routeParams, selectedFlow } = this.props
|
||||||
|
let index = 0
|
||||||
|
if (!routeParams.flowId) {
|
||||||
|
if (shift < 0) {
|
||||||
|
index = flows.length - 1
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
index = Math.min(
|
||||||
|
Math.max(0, flows.indexOf(selectedFlow) + shift),
|
||||||
|
flows.length - 1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
this.selectFlow(flows[index])
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo move to actions
|
||||||
|
*/
|
||||||
|
onMainKeyDown(e) {
|
||||||
|
var flow = this.props.selectedFlow
|
||||||
|
if (e.ctrlKey) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch (e.keyCode) {
|
||||||
|
case Key.K:
|
||||||
|
case Key.UP:
|
||||||
|
this.selectFlowRelative(-1)
|
||||||
|
break
|
||||||
|
case Key.J:
|
||||||
|
case Key.DOWN:
|
||||||
|
this.selectFlowRelative(+1)
|
||||||
|
break
|
||||||
|
case Key.SPACE:
|
||||||
|
case Key.PAGE_DOWN:
|
||||||
|
this.selectFlowRelative(+10)
|
||||||
|
break
|
||||||
|
case Key.PAGE_UP:
|
||||||
|
this.selectFlowRelative(-10)
|
||||||
|
break
|
||||||
|
case Key.END:
|
||||||
|
this.selectFlowRelative(+1e10)
|
||||||
|
break
|
||||||
|
case Key.HOME:
|
||||||
|
this.selectFlowRelative(-1e10)
|
||||||
|
break
|
||||||
|
case Key.ESC:
|
||||||
|
this.selectFlow(null)
|
||||||
|
break
|
||||||
|
case Key.H:
|
||||||
|
case Key.LEFT:
|
||||||
|
if (this.refs.flowDetails) {
|
||||||
|
this.refs.flowDetails.nextTab(-1)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case Key.L:
|
||||||
|
case Key.TAB:
|
||||||
|
case Key.RIGHT:
|
||||||
|
if (this.refs.flowDetails) {
|
||||||
|
this.refs.flowDetails.nextTab(+1)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case Key.C:
|
||||||
|
if (e.shiftKey) {
|
||||||
|
FlowActions.clear()
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case Key.D:
|
||||||
|
if (flow) {
|
||||||
|
if (e.shiftKey) {
|
||||||
|
FlowActions.duplicate(flow)
|
||||||
|
} else {
|
||||||
|
FlowActions.delete(flow)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case Key.A:
|
||||||
|
if (e.shiftKey) {
|
||||||
|
FlowActions.accept_all()
|
||||||
|
} else if (flow && flow.intercepted) {
|
||||||
|
FlowActions.accept(flow)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case Key.R:
|
||||||
|
if (!e.shiftKey && flow) {
|
||||||
|
FlowActions.replay(flow)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case Key.V:
|
||||||
|
if (e.shiftKey && flow && flow.modified) {
|
||||||
|
FlowActions.revert(flow)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case Key.E:
|
||||||
|
if (this.refs.flowDetails) {
|
||||||
|
this.refs.flowDetails.promptEdit()
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case Key.SHIFT:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
console.debug("keydown", e.keyCode)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { selectedFlow } = this.props
|
||||||
|
return (
|
||||||
|
<div className="main-view">
|
||||||
|
<FlowTable
|
||||||
|
ref="flowTable"
|
||||||
|
selectFlow={this.selectFlow}
|
||||||
|
selected={selectedFlow}
|
||||||
|
/>
|
||||||
|
{selectedFlow && [
|
||||||
|
<Splitter key="splitter"/>,
|
||||||
|
<FlowView
|
||||||
|
key="flowDetails"
|
||||||
|
ref="flowDetails"
|
||||||
|
tab={this.props.routeParams.detailTab}
|
||||||
|
query={this.props.query}
|
||||||
|
updateLocation={this.props.updateLocation}
|
||||||
|
flow={selectedFlow}
|
||||||
|
/>
|
||||||
|
]}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
state => ({
|
||||||
|
flows: state.flows.view,
|
||||||
|
filter: state.flows.filter,
|
||||||
|
highlight: state.flows.highlight,
|
||||||
|
selectedFlow: state.flows.all.byId[state.flows.selected[0]]
|
||||||
|
}),
|
||||||
|
dispatch => ({
|
||||||
|
selectFlow: flowId => dispatch(selectFlow(flowId)),
|
||||||
|
setFilter: filter => dispatch(setFilter(filter)),
|
||||||
|
setHighlight: highlight => dispatch(setHighlight(highlight))
|
||||||
|
}),
|
||||||
|
undefined,
|
||||||
|
{ withRef: true }
|
||||||
|
)(MainView)
|
|
@ -0,0 +1,163 @@
|
||||||
|
import React, { Component, PropTypes } from "react"
|
||||||
|
import ReactDOM from "react-dom"
|
||||||
|
import _ from "lodash"
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
|
||||||
|
import { Splitter } from "./common.js"
|
||||||
|
import { Header, MainMenu } from "./header.js"
|
||||||
|
import EventLog from "./eventlog.js"
|
||||||
|
import Footer from "./footer.js"
|
||||||
|
import { SettingsStore } from "../store/store.js"
|
||||||
|
import { Key } from "../utils.js"
|
||||||
|
|
||||||
|
class ProxyAppMain extends Component {
|
||||||
|
|
||||||
|
static childContextTypes = {
|
||||||
|
returnFocus: PropTypes.func.isRequired,
|
||||||
|
location: PropTypes.object.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
static contextTypes = {
|
||||||
|
router: PropTypes.object.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.settingsStore = new SettingsStore()
|
||||||
|
|
||||||
|
// Default Settings before fetch
|
||||||
|
_.extend(this.settingsStore.dict, {})
|
||||||
|
|
||||||
|
this.state = { settings: this.settingsStore.dict }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo move to actions
|
||||||
|
*/
|
||||||
|
updateLocation(pathname, queryUpdate) {
|
||||||
|
if (pathname === undefined) {
|
||||||
|
pathname = this.props.location.pathname
|
||||||
|
}
|
||||||
|
const query = this.props.location.query
|
||||||
|
for (const key of Object.keys(queryUpdate || {})) {
|
||||||
|
query[i] = queryUpdate[i] || undefined
|
||||||
|
}
|
||||||
|
this.context.router.replace({pathname, query})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo pass in with props
|
||||||
|
*/
|
||||||
|
getQuery() {
|
||||||
|
// For whatever reason, react-router always returns the same object, which makes comparing
|
||||||
|
// the current props with nextProps impossible. As a workaround, we just clone the query object.
|
||||||
|
return _.clone(this.props.location.query)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo remove settings store
|
||||||
|
* @todo connect websocket here
|
||||||
|
* @todo listen to window's key events
|
||||||
|
*/
|
||||||
|
componentDidMount() {
|
||||||
|
this.focus()
|
||||||
|
this.settingsStore.addListener("recalculate", this.onSettingsChange)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo remove settings store
|
||||||
|
* @todo disconnect websocket here
|
||||||
|
* @todo stop listening to window's key events
|
||||||
|
*/
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.settingsStore.removeListener("recalculate", this.onSettingsChange)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo move to actions
|
||||||
|
*/
|
||||||
|
onSettingsChange() {
|
||||||
|
this.setState({ settings: this.settingsStore.dict })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo use props
|
||||||
|
*/
|
||||||
|
getChildContext() {
|
||||||
|
return {
|
||||||
|
returnFocus: this.focus,
|
||||||
|
location: this.props.location
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo remove it
|
||||||
|
*/
|
||||||
|
focus() {
|
||||||
|
document.activeElement.blur()
|
||||||
|
window.getSelection().removeAllRanges()
|
||||||
|
ReactDOM.findDOMNode(this).focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo move to actions
|
||||||
|
*/
|
||||||
|
onKeydown(e) {
|
||||||
|
let name = null
|
||||||
|
|
||||||
|
switch (e.keyCode) {
|
||||||
|
case Key.I:
|
||||||
|
name = "intercept"
|
||||||
|
break
|
||||||
|
case Key.L:
|
||||||
|
name = "search"
|
||||||
|
break
|
||||||
|
case Key.H:
|
||||||
|
name = "highlight"
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
let main = this.refs.view
|
||||||
|
if (this.refs.view.getWrappedInstance) {
|
||||||
|
main = this.refs.view.getWrappedInstance()
|
||||||
|
}
|
||||||
|
if (main.onMainKeyDown) {
|
||||||
|
main.onMainKeyDown(e)
|
||||||
|
}
|
||||||
|
return // don't prevent default then
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name) {
|
||||||
|
const headerComponent = this.refs.header
|
||||||
|
headerComponent.setState({active: MainMenu}, function () {
|
||||||
|
headerComponent.refs.active.refs[name].select()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { showEventLog, location, children } = this.props
|
||||||
|
const { settings } = this.state
|
||||||
|
const query = this.getQuery()
|
||||||
|
return (
|
||||||
|
<div id="container" tabIndex="0" onKeyDown={this.onKeydown}>
|
||||||
|
<Header ref="header" settings={settings} updateLocation={this.updateLocation} query={query} />
|
||||||
|
{React.cloneElement(
|
||||||
|
children,
|
||||||
|
{ ref: "view", location, query, updateLocation: this.updateLocation }
|
||||||
|
)}
|
||||||
|
{showEventLog && [
|
||||||
|
<Splitter key="splitter" axis="y"/>,
|
||||||
|
<EventLog key="eventlog"/>
|
||||||
|
]}
|
||||||
|
<Footer settings={settings}/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
state => ({
|
||||||
|
showEventLog: state.eventLog.visible
|
||||||
|
})
|
||||||
|
)(ProxyAppMain)
|
|
@ -1,184 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
import {FlowActions} from "../actions.js";
|
|
||||||
import {Query} from "../actions.js";
|
|
||||||
import {Key} from "../utils.js";
|
|
||||||
import {Splitter} from "./common.js"
|
|
||||||
import FlowTable from "./flowtable.js";
|
|
||||||
import FlowView from "./flowview/index.js";
|
|
||||||
import {connect} from 'react-redux'
|
|
||||||
import {selectFlow, setFilter, setHighlight} from "../ducks/flows";
|
|
||||||
|
|
||||||
|
|
||||||
var MainView = React.createClass({
|
|
||||||
componentWillReceiveProps: function (nextProps) {
|
|
||||||
// Update redux store with route changes
|
|
||||||
if(nextProps.routeParams.flowId !== (nextProps.selectedFlow || {}).id) {
|
|
||||||
this.props.selectFlow(nextProps.routeParams.flowId)
|
|
||||||
}
|
|
||||||
if(nextProps.location.query[Query.SEARCH] !== nextProps.filter) {
|
|
||||||
this.props.setFilter(nextProps.location.query[Query.SEARCH], false)
|
|
||||||
}
|
|
||||||
if (nextProps.location.query[Query.HIGHLIGHT] !== nextProps.highlight) {
|
|
||||||
this.props.setHighlight(nextProps.location.query[Query.HIGHLIGHT], false)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
selectFlow: function (flow) {
|
|
||||||
// TODO: This belongs into redux
|
|
||||||
if (flow) {
|
|
||||||
let tab = this.props.routeParams.detailTab || "request";
|
|
||||||
this.props.updateLocation(`/flows/${flow.id}/${tab}`);
|
|
||||||
} else {
|
|
||||||
this.props.updateLocation("/flows");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
selectFlowRelative: function (shift) {
|
|
||||||
// TODO: This belongs into redux
|
|
||||||
let flows = this.props.flows,
|
|
||||||
index
|
|
||||||
if (!this.props.routeParams.flowId) {
|
|
||||||
if (shift < 0) {
|
|
||||||
index = flows.length - 1
|
|
||||||
} else {
|
|
||||||
index = 0
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
index = flows.indexOf(this.props.selectedFlow)
|
|
||||||
index = Math.min(
|
|
||||||
Math.max(0, index + shift),
|
|
||||||
flows.length - 1
|
|
||||||
)
|
|
||||||
}
|
|
||||||
this.selectFlow(flows[index])
|
|
||||||
},
|
|
||||||
onMainKeyDown: function (e) {
|
|
||||||
var flow = this.props.selectedFlow;
|
|
||||||
if (e.ctrlKey) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
switch (e.keyCode) {
|
|
||||||
case Key.K:
|
|
||||||
case Key.UP:
|
|
||||||
this.selectFlowRelative(-1);
|
|
||||||
break;
|
|
||||||
case Key.J:
|
|
||||||
case Key.DOWN:
|
|
||||||
this.selectFlowRelative(+1);
|
|
||||||
break;
|
|
||||||
case Key.SPACE:
|
|
||||||
case Key.PAGE_DOWN:
|
|
||||||
this.selectFlowRelative(+10);
|
|
||||||
break;
|
|
||||||
case Key.PAGE_UP:
|
|
||||||
this.selectFlowRelative(-10);
|
|
||||||
break;
|
|
||||||
case Key.END:
|
|
||||||
this.selectFlowRelative(+1e10);
|
|
||||||
break;
|
|
||||||
case Key.HOME:
|
|
||||||
this.selectFlowRelative(-1e10);
|
|
||||||
break;
|
|
||||||
case Key.ESC:
|
|
||||||
this.selectFlow(null);
|
|
||||||
break;
|
|
||||||
case Key.H:
|
|
||||||
case Key.LEFT:
|
|
||||||
if (this.refs.flowDetails) {
|
|
||||||
this.refs.flowDetails.nextTab(-1);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Key.L:
|
|
||||||
case Key.TAB:
|
|
||||||
case Key.RIGHT:
|
|
||||||
if (this.refs.flowDetails) {
|
|
||||||
this.refs.flowDetails.nextTab(+1);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Key.C:
|
|
||||||
if (e.shiftKey) {
|
|
||||||
FlowActions.clear();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Key.D:
|
|
||||||
if (flow) {
|
|
||||||
if (e.shiftKey) {
|
|
||||||
FlowActions.duplicate(flow);
|
|
||||||
} else {
|
|
||||||
FlowActions.delete(flow);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Key.A:
|
|
||||||
if (e.shiftKey) {
|
|
||||||
FlowActions.accept_all();
|
|
||||||
} else if (flow && flow.intercepted) {
|
|
||||||
FlowActions.accept(flow);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Key.R:
|
|
||||||
if (!e.shiftKey && flow) {
|
|
||||||
FlowActions.replay(flow);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Key.V:
|
|
||||||
if (e.shiftKey && flow && flow.modified) {
|
|
||||||
FlowActions.revert(flow);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Key.E:
|
|
||||||
if (this.refs.flowDetails) {
|
|
||||||
this.refs.flowDetails.promptEdit();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Key.SHIFT:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.debug("keydown", e.keyCode);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
e.preventDefault();
|
|
||||||
},
|
|
||||||
render: function () {
|
|
||||||
|
|
||||||
var details = null;
|
|
||||||
if (this.props.selectedFlow) {
|
|
||||||
details = [
|
|
||||||
<Splitter key="splitter"/>,
|
|
||||||
<FlowView
|
|
||||||
key="flowDetails"
|
|
||||||
ref="flowDetails"
|
|
||||||
tab={this.props.routeParams.detailTab}
|
|
||||||
query={this.props.query}
|
|
||||||
updateLocation={this.props.updateLocation}
|
|
||||||
flow={this.props.selectedFlow}/>
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="main-view">
|
|
||||||
<FlowTable ref="flowTable"
|
|
||||||
selectFlow={this.selectFlow}
|
|
||||||
selected={this.props.selectedFlow} />
|
|
||||||
{details}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const MainViewContainer = connect(
|
|
||||||
state => ({
|
|
||||||
flows: state.flows.view,
|
|
||||||
filter: state.flows.filter,
|
|
||||||
highlight: state.flows.highlight,
|
|
||||||
selectedFlow: state.flows.all.byId[state.flows.selected[0]]
|
|
||||||
}),
|
|
||||||
dispatch => ({
|
|
||||||
selectFlow: flowId => dispatch(selectFlow(flowId)),
|
|
||||||
setFilter: filter => dispatch(setFilter(filter)),
|
|
||||||
setHighlight: highlight => dispatch(setHighlight(highlight))
|
|
||||||
}),
|
|
||||||
undefined,
|
|
||||||
{withRef: true}
|
|
||||||
)(MainView);
|
|
||||||
|
|
||||||
export default MainViewContainer;
|
|
|
@ -1,154 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import ReactDOM from "react-dom";
|
|
||||||
import _ from "lodash";
|
|
||||||
import {connect} from 'react-redux'
|
|
||||||
import { Route, Router as ReactRouter, hashHistory, Redirect} from "react-router"
|
|
||||||
|
|
||||||
import {Splitter} from "./common.js"
|
|
||||||
import MainView from "./mainview.js";
|
|
||||||
import Footer from "./footer.js";
|
|
||||||
import {Header, MainMenu} from "./header.js";
|
|
||||||
import EventLog from "./eventlog.js"
|
|
||||||
import {SettingsStore} from "../store/store.js";
|
|
||||||
import {Key} from "../utils.js";
|
|
||||||
|
|
||||||
|
|
||||||
//TODO: Move out of here, just a stub.
|
|
||||||
var Reports = React.createClass({
|
|
||||||
render: function () {
|
|
||||||
return <div>ReportEditor</div>;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
var ProxyAppMain = React.createClass({
|
|
||||||
childContextTypes: {
|
|
||||||
returnFocus: React.PropTypes.func.isRequired,
|
|
||||||
location: React.PropTypes.object.isRequired,
|
|
||||||
},
|
|
||||||
contextTypes: {
|
|
||||||
router: React.PropTypes.object.isRequired
|
|
||||||
},
|
|
||||||
updateLocation: function (pathname, queryUpdate) {
|
|
||||||
if (pathname === undefined) {
|
|
||||||
pathname = this.props.location.pathname;
|
|
||||||
}
|
|
||||||
var query = this.props.location.query;
|
|
||||||
if (queryUpdate !== undefined) {
|
|
||||||
for (var i in queryUpdate) {
|
|
||||||
if (queryUpdate.hasOwnProperty(i)) {
|
|
||||||
query[i] = queryUpdate[i] || undefined; //falsey values shall be removed.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.context.router.replace({pathname, query});
|
|
||||||
},
|
|
||||||
getQuery: function () {
|
|
||||||
// For whatever reason, react-router always returns the same object, which makes comparing
|
|
||||||
// the current props with nextProps impossible. As a workaround, we just clone the query object.
|
|
||||||
return _.clone(this.props.location.query);
|
|
||||||
},
|
|
||||||
componentDidMount: function () {
|
|
||||||
this.focus();
|
|
||||||
this.settingsStore.addListener("recalculate", this.onSettingsChange);
|
|
||||||
},
|
|
||||||
componentWillUnmount: function () {
|
|
||||||
this.settingsStore.removeListener("recalculate", this.onSettingsChange);
|
|
||||||
},
|
|
||||||
onSettingsChange: function () {
|
|
||||||
this.setState({ settings: this.settingsStore.dict });
|
|
||||||
},
|
|
||||||
getChildContext: function () {
|
|
||||||
return {
|
|
||||||
returnFocus: this.focus,
|
|
||||||
location: this.props.location
|
|
||||||
};
|
|
||||||
},
|
|
||||||
getInitialState: function () {
|
|
||||||
var settingsStore = new SettingsStore();
|
|
||||||
|
|
||||||
this.settingsStore = settingsStore;
|
|
||||||
// Default Settings before fetch
|
|
||||||
_.extend(settingsStore.dict, {});
|
|
||||||
return {
|
|
||||||
settings: settingsStore.dict,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
focus: function () {
|
|
||||||
document.activeElement.blur();
|
|
||||||
window.getSelection().removeAllRanges();
|
|
||||||
ReactDOM.findDOMNode(this).focus();
|
|
||||||
},
|
|
||||||
getMainComponent: function () {
|
|
||||||
return this.refs.view.getWrappedInstance ? this.refs.view.getWrappedInstance() : this.refs.view;
|
|
||||||
},
|
|
||||||
onKeydown: function (e) {
|
|
||||||
|
|
||||||
var selectFilterInput = function (name) {
|
|
||||||
var headerComponent = this.refs.header;
|
|
||||||
headerComponent.setState({active: MainMenu}, function () {
|
|
||||||
headerComponent.refs.active.refs[name].select();
|
|
||||||
});
|
|
||||||
}.bind(this);
|
|
||||||
|
|
||||||
switch (e.keyCode) {
|
|
||||||
case Key.I:
|
|
||||||
selectFilterInput("intercept");
|
|
||||||
break;
|
|
||||||
case Key.L:
|
|
||||||
selectFilterInput("search");
|
|
||||||
break;
|
|
||||||
case Key.H:
|
|
||||||
selectFilterInput("highlight");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
var main = this.getMainComponent();
|
|
||||||
if (main.onMainKeyDown) {
|
|
||||||
main.onMainKeyDown(e);
|
|
||||||
}
|
|
||||||
return; // don't prevent default then
|
|
||||||
}
|
|
||||||
e.preventDefault();
|
|
||||||
},
|
|
||||||
render: function () {
|
|
||||||
var query = this.getQuery();
|
|
||||||
var eventlog;
|
|
||||||
if (this.props.showEventLog) {
|
|
||||||
eventlog = [
|
|
||||||
<Splitter key="splitter" axis="y"/>,
|
|
||||||
<EventLog key="eventlog"/>
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
eventlog = null;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div id="container" tabIndex="0" onKeyDown={this.onKeydown}>
|
|
||||||
<Header ref="header" settings={this.state.settings} updateLocation={this.updateLocation} query={query} />
|
|
||||||
{React.cloneElement(
|
|
||||||
this.props.children,
|
|
||||||
{ ref: "view", location: this.props.location , updateLocation: this.updateLocation, query }
|
|
||||||
)}
|
|
||||||
{eventlog}
|
|
||||||
<Footer settings={this.state.settings}/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const AppContainer = connect(
|
|
||||||
state => ({
|
|
||||||
showEventLog: state.eventLog.visible
|
|
||||||
})
|
|
||||||
)(ProxyAppMain);
|
|
||||||
|
|
||||||
|
|
||||||
export var App = (
|
|
||||||
<ReactRouter history={hashHistory}>
|
|
||||||
<Redirect from="/" to="/flows" />
|
|
||||||
<Route path="/" component={AppContainer}>
|
|
||||||
<Route path="flows" component={MainView}/>
|
|
||||||
<Route path="flows/:flowId/:detailTab" component={MainView}/>
|
|
||||||
<Route path="reports" component={Reports}/>
|
|
||||||
</Route>
|
|
||||||
</ReactRouter>
|
|
||||||
);
|
|
Loading…
Reference in New Issue