diff options
author | Maximilian Hils <git@maximilianhils.com> | 2016-06-04 18:53:41 -0700 |
---|---|---|
committer | Maximilian Hils <git@maximilianhils.com> | 2016-06-04 18:53:41 -0700 |
commit | d53a2de0ba69bea6c7aefa87782ad249cfb4ea76 (patch) | |
tree | 25e474bf95e4f83022708e84184d8d7d56a6e3ce /web/src/js/components | |
parent | e880f532ad3c66ebfded4655b7fa67a367a83cc7 (diff) | |
download | mitmproxy-d53a2de0ba69bea6c7aefa87782ad249cfb4ea76.tar.gz mitmproxy-d53a2de0ba69bea6c7aefa87782ad249cfb4ea76.tar.bz2 mitmproxy-d53a2de0ba69bea6c7aefa87782ad249cfb4ea76.zip |
web: completely move flow state to redux
Diffstat (limited to 'web/src/js/components')
-rw-r--r-- | web/src/js/components/flowtable.js | 64 | ||||
-rw-r--r-- | web/src/js/components/mainview.js | 149 | ||||
-rw-r--r-- | web/src/js/components/proxyapp.js | 8 |
3 files changed, 81 insertions, 140 deletions
diff --git a/web/src/js/components/flowtable.js b/web/src/js/components/flowtable.js index 1a616eee..0241cd78 100644 --- a/web/src/js/components/flowtable.js +++ b/web/src/js/components/flowtable.js @@ -8,12 +8,14 @@ import shallowEqual from "shallowequal"; import AutoScroll from "./helpers/AutoScroll"; import {calcVScroll} from "./helpers/VirtualScroll"; import flowtable_columns from "./flowtable-columns.js"; +import Filt from "../filt/filt"; + FlowRow.propTypes = { selectFlow: React.PropTypes.func.isRequired, columns: React.PropTypes.array.isRequired, flow: React.PropTypes.object.isRequired, - highlighted: React.PropTypes.bool, + highlight: React.PropTypes.string, selected: React.PropTypes.bool, }; @@ -22,7 +24,7 @@ function FlowRow(props) { const className = classNames({ "selected": props.selected, - "highlighted": props.highlighted, + "highlighted": props.highlight && parseFilter(props.highlight)(flow), "intercepted": flow.intercepted, "has-request": flow.request, "has-response": flow.response, @@ -39,9 +41,12 @@ function FlowRow(props) { const FlowRowContainer = connect( (state, ownProps) => ({ - flow: state.flows.all.byId[ownProps.flowId] + flow: state.flows.all.byId[ownProps.flowId], + highlight: state.flows.highlight, + selected: state.flows.selected.indexOf(ownProps.flowId) >= 0 }), - dispatch => ({ + (dispatch, ownProps) => ({ + }) )(FlowRow); @@ -102,10 +107,6 @@ class FlowTableHead extends React.Component { class FlowTable extends React.Component { - static contextTypes = { - view: React.PropTypes.object.isRequired, - }; - static propTypes = { rowHeight: React.PropTypes.number, }; @@ -117,26 +118,23 @@ class FlowTable extends React.Component { constructor(props, context) { super(props, context); - this.state = { flows: [], vScroll: calcVScroll() }; + this.state = { vScroll: calcVScroll() }; - this.onChange = this.onChange.bind(this); this.onViewportUpdate = this.onViewportUpdate.bind(this); } componentWillMount() { window.addEventListener("resize", this.onViewportUpdate); - this.context.view.addListener("add", this.onChange); - this.context.view.addListener("update", this.onChange); - this.context.view.addListener("remove", this.onChange); - this.context.view.addListener("recalculate", this.onChange); } componentWillUnmount() { window.removeEventListener("resize", this.onViewportUpdate); - this.context.view.removeListener("add", this.onChange); - this.context.view.removeListener("update", this.onChange); - this.context.view.removeListener("remove", this.onChange); - this.context.view.removeListener("recalculate", this.onChange); + } + + componentWillReceiveProps(nextProps) { + if(nextProps.selected && nextProps.selected !== this.props.selected){ + window.setTimeout(() => this.scrollIntoView(nextProps.selected), 1) + } } componentDidUpdate() { @@ -150,7 +148,7 @@ class FlowTable extends React.Component { const vScroll = calcVScroll({ viewportTop, viewportHeight: viewport.offsetHeight, - itemCount: this.state.flows.length, + itemCount: this.props.flows.length, rowHeight: this.props.rowHeight, }); @@ -160,13 +158,9 @@ class FlowTable extends React.Component { } } - onChange() { - this.setState({ flows: this.context.view.list }); - } - scrollIntoView(flow) { const viewport = ReactDOM.findDOMNode(this); - const index = this.context.view.indexOf(flow); + const index = this.props.flows.indexOf(flow); const rowHeight = this.props.rowHeight; const head = ReactDOM.findDOMNode(this.refs.head); @@ -188,8 +182,7 @@ class FlowTable extends React.Component { render() { const vScroll = this.state.vScroll; - const highlight = this.context.view._highlight; - const flows = this.state.flows.slice(vScroll.start, vScroll.end); + const flows = this.props.flows.slice(vScroll.start, vScroll.end); const transform = `translate(0,${this.state.viewportTop}px)`; @@ -206,11 +199,9 @@ class FlowTable extends React.Component { <tr style={{ height: vScroll.paddingTop }}></tr> {flows.map(flow => ( <FlowRowContainer - flowId={flow.id} key={flow.id} + flowId={flow.id} columns={flowtable_columns} - selected={flow === this.props.selected} - highlighted={highlight && highlight[flow.id]} selectFlow={this.props.selectFlow} /> ))} @@ -222,4 +213,17 @@ class FlowTable extends React.Component { } } -export default AutoScroll(FlowTable); +FlowTable = AutoScroll(FlowTable) + + +const parseFilter = _.memoize(Filt.parse) + +const FlowTableContainer = connect( + state => ({ + flows: state.flows.view, + }), + dispatch => ({ + }) +)(FlowTable) + +export default FlowTableContainer; diff --git a/web/src/js/components/mainview.js b/web/src/js/components/mainview.js index 964e82db..22895991 100644 --- a/web/src/js/components/mainview.js +++ b/web/src/js/components/mainview.js @@ -3,128 +3,59 @@ import React from "react"; import {FlowActions} from "../actions.js"; import {Query} from "../actions.js"; import {Key} from "../utils.js"; -import {StoreView} from "../store/view.js"; -import Filt from "../filt/filt.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({ - contextTypes: { - flowStore: React.PropTypes.object.isRequired, - }, - childContextTypes: { - view: React.PropTypes.object.isRequired, - }, - getChildContext: function () { - return { - view: this.state.view - }; - }, - getInitialState: function () { - var sortKeyFun = false; - var view = new StoreView(this.context.flowStore, this.getViewFilt(), sortKeyFun); - view.addListener("recalculate", this.onRecalculate); - view.addListener("add", this.onUpdate); - view.addListener("update", this.onUpdate); - view.addListener("remove", this.onUpdate); - view.addListener("remove", this.onRemove); - - return { - view: view, - sortKeyFun: sortKeyFun - }; - }, - componentWillUnmount: function () { - this.state.view.close(); - }, - getViewFilt: function () { - try { - var filtStr = this.props.query[Query.SEARCH]; - var filt = filtStr ? Filt.parse(filtStr) : () => true; - var highlightStr = this.props.query[Query.HIGHLIGHT]; - var highlight = highlightStr ? Filt.parse(highlightStr) : () => false; - } catch (e) { - console.error("Error when processing filter: " + e); - } - var fun = function filter_and_highlight(flow) { - if (!this._highlight) { - this._highlight = {}; - } - this._highlight[flow.id] = highlight(flow); - return filt(flow); - }; - fun.highlightStr = highlightStr; - fun.filtStr = filtStr; - return fun; - }, +var MainView = React.createClass({ componentWillReceiveProps: function (nextProps) { - var filterChanged = this.state.view.filt.filtStr !== nextProps.location.query[Query.SEARCH]; - var highlightChanged = this.state.view.filt.highlightStr !== nextProps.location.query[Query.HIGHLIGHT]; - if (filterChanged || highlightChanged) { - this.state.view.recalculate(this.getViewFilt(), this.state.sortKeyFun); + // Update redux store with route changes + if(nextProps.routeParams.flowId !== (nextProps.selectedFlow || {}).id) { + this.props.selectFlow(nextProps.routeParams.flowId) } - }, - onRecalculate: function () { - this.forceUpdate(); - var selected = this.getSelected(); - if (selected) { - this.refs.flowTable.scrollIntoView(selected); + if(nextProps.location.query[Query.SEARCH] !== nextProps.filter) { + this.props.setFilter(nextProps.location.query[Query.SEARCH], false) } - }, - onUpdate: function (flow) { - if (flow.id === this.props.routeParams.flowId) { - this.forceUpdate(); - } - }, - onRemove: function (flow_id, index) { - if (flow_id === this.props.routeParams.flowId) { - var flow_to_select = this.state.view.list[Math.min(index, this.state.view.list.length - 1)]; - this.selectFlow(flow_to_select); + if (nextProps.location.query[Query.HIGHLIGHT] !== nextProps.highlight) { + this.props.setHighlight(nextProps.location.query[Query.HIGHLIGHT], false) } }, setSortKeyFun: function (sortKeyFun) { - this.setState({ - sortKeyFun: sortKeyFun - }); - this.state.view.recalculate(this.getViewFilt(), sortKeyFun); + // FIXME: Move to redux. This requires that sortKeyFun is not a function anymore. }, selectFlow: function (flow) { + // TODO: This belongs into redux if (flow) { - var tab = this.props.routeParams.detailTab || "request"; + let tab = this.props.routeParams.detailTab || "request"; this.props.updateLocation(`/flows/${flow.id}/${tab}`); - this.refs.flowTable.scrollIntoView(flow); } else { this.props.updateLocation("/flows"); } }, selectFlowRelative: function (shift) { - var flows = this.state.view.list; - var index; + // TODO: This belongs into redux + let flows = this.props.flows, + index if (!this.props.routeParams.flowId) { if (shift < 0) { - index = flows.length - 1; + index = flows.length - 1 } else { - index = 0; + index = 0 } } else { - var currFlowId = this.props.routeParams.flowId; - var i = flows.length; - while (i--) { - if (flows[i].id === currFlowId) { - index = i; - break; - } - } + index = flows.indexOf(this.props.selectedFlow) index = Math.min( Math.max(0, index + shift), - flows.length - 1); + flows.length - 1 + ) } - this.selectFlow(flows[index]); + this.selectFlow(flows[index]) }, onMainKeyDown: function (e) { - var flow = this.getSelected(); + var flow = this.props.selectedFlow; if (e.ctrlKey) { return; } @@ -210,14 +141,10 @@ var MainView = React.createClass({ } e.preventDefault(); }, - getSelected: function () { - return this.context.flowStore.get(this.props.routeParams.flowId); - }, render: function () { - var selected = this.getSelected(); - var details; - if (selected) { + var details = null; + if (this.props.selectedFlow) { details = [ <Splitter key="splitter"/>, <FlowView @@ -226,10 +153,8 @@ var MainView = React.createClass({ tab={this.props.routeParams.detailTab} query={this.props.query} updateLocation={this.props.updateLocation} - flow={selected}/> - ]; - } else { - details = null; + flow={this.props.selectedFlow}/> + ] } return ( @@ -237,11 +162,27 @@ var MainView = React.createClass({ <FlowTable ref="flowTable" selectFlow={this.selectFlow} setSortKeyFun={this.setSortKeyFun} - selected={selected} /> + selected={this.props.selectedFlow} /> {details} </div> ); } }); -export default MainView; +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; diff --git a/web/src/js/components/proxyapp.js b/web/src/js/components/proxyapp.js index 9e4bd0a4..e4489e18 100644 --- a/web/src/js/components/proxyapp.js +++ b/web/src/js/components/proxyapp.js @@ -9,7 +9,7 @@ import MainView from "./mainview.js"; import Footer from "./footer.js"; import {Header, MainMenu} from "./header.js"; import EventLog from "./eventlog.js" -import {FlowStore, SettingsStore} from "../store/store.js"; +import {SettingsStore} from "../store/store.js"; import {Key} from "../utils.js"; @@ -23,7 +23,6 @@ var Reports = React.createClass({ var ProxyAppMain = React.createClass({ childContextTypes: { - flowStore: React.PropTypes.object.isRequired, returnFocus: React.PropTypes.func.isRequired, location: React.PropTypes.object.isRequired, }, @@ -61,13 +60,11 @@ var ProxyAppMain = React.createClass({ }, getChildContext: function () { return { - flowStore: this.state.flowStore, returnFocus: this.focus, location: this.props.location }; }, getInitialState: function () { - var flowStore = new FlowStore(); var settingsStore = new SettingsStore(); this.settingsStore = settingsStore; @@ -75,7 +72,6 @@ var ProxyAppMain = React.createClass({ _.extend(settingsStore.dict, {}); return { settings: settingsStore.dict, - flowStore: flowStore, }; }, focus: function () { @@ -84,7 +80,7 @@ var ProxyAppMain = React.createClass({ ReactDOM.findDOMNode(this).focus(); }, getMainComponent: function () { - return this.refs.view; + return this.refs.view.getWrappedInstance ? this.refs.view.getWrappedInstance() : this.refs.view; }, onKeydown: function (e) { |