From 1913975fa60c76bfb7e79a908b18e7e93793f71f Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Fri, 27 Mar 2015 21:58:04 +0100 Subject: web: use contexts to pass down stores. Using contexts frees us from the contracts we have using props - namely, we can assume them to be constant for the lifetime of the object. --- web/src/js/components/common.js | 55 +++++++++++++++++++--------- web/src/js/components/eventlog.js | 43 +++++++++------------- web/src/js/components/flowtable.js | 42 +++++++++------------- web/src/js/components/flowview/index.js | 2 +- web/src/js/components/footer.js | 6 ++-- web/src/js/components/header.js | 10 +++--- web/src/js/components/mainview.js | 63 ++++++++++++++------------------- web/src/js/components/proxyapp.js | 44 +++++++++++------------ 8 files changed, 129 insertions(+), 136 deletions(-) (limited to 'web/src/js/components') diff --git a/web/src/js/components/common.js b/web/src/js/components/common.js index 433e4f10..b0aa0977 100644 --- a/web/src/js/components/common.js +++ b/web/src/js/components/common.js @@ -7,8 +7,8 @@ var AutoScrollMixin = { componentWillUpdate: function () { var node = this.getDOMNode(); this._shouldScrollBottom = ( - node.scrollTop !== 0 && - node.scrollTop + node.clientHeight === node.scrollHeight + node.scrollTop !== 0 && + node.scrollTop + node.clientHeight === node.scrollHeight ); }, componentDidUpdate: function () { @@ -29,32 +29,54 @@ var StickyHeadMixin = { } }; +var SettingsState = { + contextTypes: { + settingsStore: React.PropTypes.object.isRequired + }, + getInitialState: function () { + return { + settings: this.context.settingsStore.dict + }; + }, + componentDidMount: function () { + this.context.settingsStore.addListener("recalculate", this.onSettingsChange); + }, + componentWillUnmount: function () { + this.context.settingsStore.removeListener("recalculate", this.onSettingsChange); + }, + onSettingsChange: function () { + this.setState({ + settings: this.context.settingsStore.dict + }); + }, +}; + var ChildFocus = { - contextTypes: { - returnFocus: React.PropTypes.func - } + contextTypes: { + returnFocus: React.PropTypes.func + } }; var Navigation = _.extend({}, ReactRouter.Navigation, { setQuery: function (dict) { var q = this.context.router.getCurrentQuery(); - for(var i in dict){ - if(dict.hasOwnProperty(i)){ + for (var i in dict) { + if (dict.hasOwnProperty(i)) { q[i] = dict[i] || undefined; //falsey values shall be removed. } } this.replaceWith(this.context.router.getCurrentPath(), this.context.router.getCurrentParams(), q); }, - replaceWith: function(routeNameOrPath, params, query) { - if(routeNameOrPath === undefined){ + replaceWith: function (routeNameOrPath, params, query) { + if (routeNameOrPath === undefined) { routeNameOrPath = this.context.router.getCurrentPath(); } - if(params === undefined){ + if (params === undefined) { params = this.context.router.getCurrentParams(); } - if(query === undefined) { + if (query === undefined) { query = this.context.router.getCurrentQuery(); } @@ -65,13 +87,13 @@ var Navigation = _.extend({}, ReactRouter.Navigation, { // react-router is fairly good at changing its API regularly. // We keep the old method for now - if it should turn out that their changes are permanent, // we may remove this mixin and access react-router directly again. -var State = _.extend({}, ReactRouter.State, { - getQuery: function(){ +var RouterState = _.extend({}, ReactRouter.State, { + 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.context.router.getCurrentQuery()); }, - getParams: function(){ + getParams: function () { return _.clone(this.context.router.getCurrentParams()); } }); @@ -183,9 +205,10 @@ var Splitter = React.createClass({ module.exports = { ChildFocus: ChildFocus, - State: State, + RouterState: RouterState, Navigation: Navigation, StickyHeadMixin: StickyHeadMixin, AutoScrollMixin: AutoScrollMixin, - Splitter: Splitter + Splitter: Splitter, + SettingsState: SettingsState }; \ No newline at end of file diff --git a/web/src/js/components/eventlog.js b/web/src/js/components/eventlog.js index de69462b..65024712 100644 --- a/web/src/js/components/eventlog.js +++ b/web/src/js/components/eventlog.js @@ -31,46 +31,37 @@ var LogMessage = React.createClass({ }); var EventLogContents = React.createClass({ + contextTypes: { + eventStore: React.PropTypes.object.isRequired + }, mixins: [common.AutoScrollMixin, VirtualScrollMixin], getInitialState: function () { - return { - log: [] - }; - }, - componentWillMount: function () { - this.openView(this.props.eventStore); - }, - componentWillUnmount: function () { - this.closeView(); - }, - openView: function (store) { - var view = new views.StoreView(store, function (entry) { + var filterFn = function (entry) { return this.props.filter[entry.level]; - }.bind(this)); - this.setState({ - view: view - }); - + }; + var view = new views.StoreView(this.context.eventStore, filterFn.bind(this)); view.addListener("add", this.onEventLogChange); view.addListener("recalculate", this.onEventLogChange); + + return { + log: view.list, + view: view + }; }, - closeView: function () { + componentWillUnmount: function () { this.state.view.close(); }, + filter: function (entry) { + return this.props.filter[entry.level]; + }, onEventLogChange: function () { - this.setState({ - log: this.state.view.list - }); + this.forceUpdate(); }, componentWillReceiveProps: function (nextProps) { if (nextProps.filter !== this.props.filter) { this.props.filter = nextProps.filter; // Dirty: Make sure that view filter sees the update. this.state.view.recalculate(); } - if (nextProps.eventStore !== this.props.eventStore) { - this.closeView(); - this.openView(nextProps.eventStore); - } }, getDefaultProps: function () { return { @@ -149,7 +140,7 @@ var EventLog = React.createClass({ - + ); } diff --git a/web/src/js/components/flowtable.js b/web/src/js/components/flowtable.js index 4217786a..609034f6 100644 --- a/web/src/js/components/flowtable.js +++ b/web/src/js/components/flowtable.js @@ -108,33 +108,25 @@ var ROW_HEIGHT = 32; var FlowTable = React.createClass({ mixins: [common.StickyHeadMixin, common.AutoScrollMixin, VirtualScrollMixin], + contextTypes: { + view: React.PropTypes.object.isRequired + }, getInitialState: function () { return { columns: flowtable_columns }; }, - _listen: function(view){ - if(!view){ - return; - } - view.addListener("add", this.onChange); - view.addListener("update", this.onChange); - view.addListener("remove", this.onChange); - view.addListener("recalculate", this.onChange); - }, componentWillMount: function () { - this._listen(this.props.view); + 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); }, - componentWillReceiveProps: function (nextProps) { - if (nextProps.view !== this.props.view) { - if (this.props.view) { - this.props.view.removeListener("add"); - this.props.view.removeListener("update"); - this.props.view.removeListener("remove"); - this.props.view.removeListener("recalculate"); - } - this._listen(nextProps.view); - } + componentWillUnmount: function(){ + 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); }, getDefaultProps: function () { return { @@ -150,7 +142,7 @@ var FlowTable = React.createClass({ }, scrollIntoView: function (flow) { this.scrollRowIntoView( - this.props.view.index(flow), + this.context.view.index(flow), this.refs.body.getDOMNode().offsetTop ); }, @@ -158,8 +150,8 @@ var FlowTable = React.createClass({ var selected = (flow === this.props.selected); var highlighted = ( - this.props.view._highlight && - this.props.view._highlight[flow.id] + this.context.view._highlight && + this.context.view._highlight[flow.id] ); return ; }, render: function () { - //console.log("render flowtable", this.state.start, this.state.stop, this.props.selected); - var flows = this.props.view ? this.props.view.list : []; - + var flows = this.context.view.list; var rows = this.renderRows(flows); return ( diff --git a/web/src/js/components/flowview/index.js b/web/src/js/components/flowview/index.js index 0c31aca5..4214714e 100644 --- a/web/src/js/components/flowview/index.js +++ b/web/src/js/components/flowview/index.js @@ -14,7 +14,7 @@ var allTabs = { }; var FlowView = React.createClass({ - mixins: [common.StickyHeadMixin, common.Navigation, common.State], + mixins: [common.StickyHeadMixin, common.Navigation, common.RouterState], getTabs: function (flow) { var tabs = []; ["request", "response", "error"].forEach(function (e) { diff --git a/web/src/js/components/footer.js b/web/src/js/components/footer.js index d04fb615..52d6d0ad 100644 --- a/web/src/js/components/footer.js +++ b/web/src/js/components/footer.js @@ -1,9 +1,11 @@ var React = require("react"); +var common = require("./common.js"); var Footer = React.createClass({ + mixins: [common.SettingsState], render: function () { - var mode = this.props.settings.mode; - var intercept = this.props.settings.intercept; + var mode = this.state.settings.mode; + var intercept = this.state.settings.intercept; return (