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.
This commit is contained in:
Maximilian Hils 2015-03-27 21:58:04 +01:00
parent f39e6c5c18
commit 1913975fa6
10 changed files with 261 additions and 273 deletions

View File

@ -468,8 +468,8 @@ var AutoScrollMixin = {
componentWillUpdate: function () { componentWillUpdate: function () {
var node = this.getDOMNode(); var node = this.getDOMNode();
this._shouldScrollBottom = ( this._shouldScrollBottom = (
node.scrollTop !== 0 && node.scrollTop !== 0 &&
node.scrollTop + node.clientHeight === node.scrollHeight node.scrollTop + node.clientHeight === node.scrollHeight
); );
}, },
componentDidUpdate: function () { componentDidUpdate: function () {
@ -490,32 +490,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 = { var ChildFocus = {
contextTypes: { contextTypes: {
returnFocus: React.PropTypes.func returnFocus: React.PropTypes.func
} }
}; };
var Navigation = _.extend({}, ReactRouter.Navigation, { var Navigation = _.extend({}, ReactRouter.Navigation, {
setQuery: function (dict) { setQuery: function (dict) {
var q = this.context.router.getCurrentQuery(); var q = this.context.router.getCurrentQuery();
for(var i in dict){ for (var i in dict) {
if(dict.hasOwnProperty(i)){ if (dict.hasOwnProperty(i)) {
q[i] = dict[i] || undefined; //falsey values shall be removed. q[i] = dict[i] || undefined; //falsey values shall be removed.
} }
} }
this.replaceWith(this.context.router.getCurrentPath(), this.context.router.getCurrentParams(), q); this.replaceWith(this.context.router.getCurrentPath(), this.context.router.getCurrentParams(), q);
}, },
replaceWith: function(routeNameOrPath, params, query) { replaceWith: function (routeNameOrPath, params, query) {
if(routeNameOrPath === undefined){ if (routeNameOrPath === undefined) {
routeNameOrPath = this.context.router.getCurrentPath(); routeNameOrPath = this.context.router.getCurrentPath();
} }
if(params === undefined){ if (params === undefined) {
params = this.context.router.getCurrentParams(); params = this.context.router.getCurrentParams();
} }
if(query === undefined) { if (query === undefined) {
query = this.context.router.getCurrentQuery(); query = this.context.router.getCurrentQuery();
} }
@ -526,13 +548,13 @@ var Navigation = _.extend({}, ReactRouter.Navigation, {
// react-router is fairly good at changing its API regularly. // 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 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. // we may remove this mixin and access react-router directly again.
var State = _.extend({}, ReactRouter.State, { var RouterState = _.extend({}, ReactRouter.State, {
getQuery: function(){ getQuery: function () {
// For whatever reason, react-router always returns the same object, which makes comparing // 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. // the current props with nextProps impossible. As a workaround, we just clone the query object.
return _.clone(this.context.router.getCurrentQuery()); return _.clone(this.context.router.getCurrentQuery());
}, },
getParams: function(){ getParams: function () {
return _.clone(this.context.router.getCurrentParams()); return _.clone(this.context.router.getCurrentParams());
} }
}); });
@ -644,11 +666,12 @@ var Splitter = React.createClass({displayName: "Splitter",
module.exports = { module.exports = {
ChildFocus: ChildFocus, ChildFocus: ChildFocus,
State: State, RouterState: RouterState,
Navigation: Navigation, Navigation: Navigation,
StickyHeadMixin: StickyHeadMixin, StickyHeadMixin: StickyHeadMixin,
AutoScrollMixin: AutoScrollMixin, AutoScrollMixin: AutoScrollMixin,
Splitter: Splitter Splitter: Splitter,
SettingsState: SettingsState
}; };
},{"lodash":"lodash","react":"react","react-router":"react-router"}],5:[function(require,module,exports){ },{"lodash":"lodash","react":"react","react-router":"react-router"}],5:[function(require,module,exports){
@ -685,46 +708,37 @@ var LogMessage = React.createClass({displayName: "LogMessage",
}); });
var EventLogContents = React.createClass({displayName: "EventLogContents", var EventLogContents = React.createClass({displayName: "EventLogContents",
contextTypes: {
eventStore: React.PropTypes.object.isRequired
},
mixins: [common.AutoScrollMixin, VirtualScrollMixin], mixins: [common.AutoScrollMixin, VirtualScrollMixin],
getInitialState: function () { getInitialState: function () {
return { var filterFn = function (entry) {
log: []
};
},
componentWillMount: function () {
this.openView(this.props.eventStore);
},
componentWillUnmount: function () {
this.closeView();
},
openView: function (store) {
var view = new views.StoreView(store, function (entry) {
return this.props.filter[entry.level]; return this.props.filter[entry.level];
}.bind(this)); };
this.setState({ var view = new views.StoreView(this.context.eventStore, filterFn.bind(this));
view: view
});
view.addListener("add", this.onEventLogChange); view.addListener("add", this.onEventLogChange);
view.addListener("recalculate", this.onEventLogChange); view.addListener("recalculate", this.onEventLogChange);
return {
log: view.list,
view: view
};
}, },
closeView: function () { componentWillUnmount: function () {
this.state.view.close(); this.state.view.close();
}, },
filter: function (entry) {
return this.props.filter[entry.level];
},
onEventLogChange: function () { onEventLogChange: function () {
this.setState({ this.forceUpdate();
log: this.state.view.list
});
}, },
componentWillReceiveProps: function (nextProps) { componentWillReceiveProps: function (nextProps) {
if (nextProps.filter !== this.props.filter) { if (nextProps.filter !== this.props.filter) {
this.props.filter = nextProps.filter; // Dirty: Make sure that view filter sees the update. this.props.filter = nextProps.filter; // Dirty: Make sure that view filter sees the update.
this.state.view.recalculate(); this.state.view.recalculate();
} }
if (nextProps.eventStore !== this.props.eventStore) {
this.closeView();
this.openView(nextProps.eventStore);
}
}, },
getDefaultProps: function () { getDefaultProps: function () {
return { return {
@ -803,7 +817,7 @@ var EventLog = React.createClass({displayName: "EventLog",
) )
), ),
React.createElement(EventLogContents, {filter: this.state.filter, eventStore: this.props.eventStore}) React.createElement(EventLogContents, {filter: this.state.filter})
) )
); );
} }
@ -1125,33 +1139,25 @@ var ROW_HEIGHT = 32;
var FlowTable = React.createClass({displayName: "FlowTable", var FlowTable = React.createClass({displayName: "FlowTable",
mixins: [common.StickyHeadMixin, common.AutoScrollMixin, VirtualScrollMixin], mixins: [common.StickyHeadMixin, common.AutoScrollMixin, VirtualScrollMixin],
contextTypes: {
view: React.PropTypes.object.isRequired
},
getInitialState: function () { getInitialState: function () {
return { return {
columns: flowtable_columns 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 () { 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) { componentWillUnmount: function(){
if (nextProps.view !== this.props.view) { this.context.view.removeListener("add", this.onChange);
if (this.props.view) { this.context.view.removeListener("update", this.onChange);
this.props.view.removeListener("add"); this.context.view.removeListener("remove", this.onChange);
this.props.view.removeListener("update"); this.context.view.removeListener("recalculate", this.onChange);
this.props.view.removeListener("remove");
this.props.view.removeListener("recalculate");
}
this._listen(nextProps.view);
}
}, },
getDefaultProps: function () { getDefaultProps: function () {
return { return {
@ -1167,7 +1173,7 @@ var FlowTable = React.createClass({displayName: "FlowTable",
}, },
scrollIntoView: function (flow) { scrollIntoView: function (flow) {
this.scrollRowIntoView( this.scrollRowIntoView(
this.props.view.index(flow), this.context.view.index(flow),
this.refs.body.getDOMNode().offsetTop this.refs.body.getDOMNode().offsetTop
); );
}, },
@ -1175,8 +1181,8 @@ var FlowTable = React.createClass({displayName: "FlowTable",
var selected = (flow === this.props.selected); var selected = (flow === this.props.selected);
var highlighted = var highlighted =
( (
this.props.view._highlight && this.context.view._highlight &&
this.props.view._highlight[flow.id] this.context.view._highlight[flow.id]
); );
return React.createElement(FlowRow, {key: flow.id, return React.createElement(FlowRow, {key: flow.id,
@ -1189,9 +1195,7 @@ var FlowTable = React.createClass({displayName: "FlowTable",
); );
}, },
render: function () { render: function () {
//console.log("render flowtable", this.state.start, this.state.stop, this.props.selected); var flows = this.context.view.list;
var flows = this.props.view ? this.props.view.list : [];
var rows = this.renderRows(flows); var rows = this.renderRows(flows);
return ( return (
@ -1653,7 +1657,7 @@ var allTabs = {
}; };
var FlowView = React.createClass({displayName: "FlowView", var FlowView = React.createClass({displayName: "FlowView",
mixins: [common.StickyHeadMixin, common.Navigation, common.State], mixins: [common.StickyHeadMixin, common.Navigation, common.RouterState],
getTabs: function (flow) { getTabs: function (flow) {
var tabs = []; var tabs = [];
["request", "response", "error"].forEach(function (e) { ["request", "response", "error"].forEach(function (e) {
@ -2171,11 +2175,13 @@ module.exports = Nav;
},{"../../actions.js":2,"react":"react"}],13:[function(require,module,exports){ },{"../../actions.js":2,"react":"react"}],13:[function(require,module,exports){
var React = require("react"); var React = require("react");
var common = require("./common.js");
var Footer = React.createClass({displayName: "Footer", var Footer = React.createClass({displayName: "Footer",
mixins: [common.SettingsState],
render: function () { render: function () {
var mode = this.props.settings.mode; var mode = this.state.settings.mode;
var intercept = this.props.settings.intercept; var intercept = this.state.settings.intercept;
return ( return (
React.createElement("footer", null, React.createElement("footer", null,
mode != "regular" ? React.createElement("span", {className: "label label-success"}, mode, " mode") : null, mode != "regular" ? React.createElement("span", {className: "label label-success"}, mode, " mode") : null,
@ -2188,7 +2194,7 @@ var Footer = React.createClass({displayName: "Footer",
module.exports = Footer; module.exports = Footer;
},{"react":"react"}],14:[function(require,module,exports){ },{"./common.js":4,"react":"react"}],14:[function(require,module,exports){
var React = require("react"); var React = require("react");
var $ = require("jquery"); var $ = require("jquery");
@ -2348,7 +2354,7 @@ var FilterInput = React.createClass({displayName: "FilterInput",
}); });
var MainMenu = React.createClass({displayName: "MainMenu", var MainMenu = React.createClass({displayName: "MainMenu",
mixins: [common.Navigation, common.State], mixins: [common.Navigation, common.RouterState, common.SettingsState],
statics: { statics: {
title: "Start", title: "Start",
route: "flows" route: "flows"
@ -2369,7 +2375,7 @@ var MainMenu = React.createClass({displayName: "MainMenu",
render: function () { render: function () {
var filter = this.getQuery()[Query.FILTER] || ""; var filter = this.getQuery()[Query.FILTER] || "";
var highlight = this.getQuery()[Query.HIGHLIGHT] || ""; var highlight = this.getQuery()[Query.HIGHLIGHT] || "";
var intercept = this.props.settings.intercept || ""; var intercept = this.state.settings.intercept || "";
return ( return (
React.createElement("div", null, React.createElement("div", null,
@ -2405,7 +2411,7 @@ var ViewMenu = React.createClass({displayName: "ViewMenu",
title: "View", title: "View",
route: "flows" route: "flows"
}, },
mixins: [common.Navigation, common.State], mixins: [common.Navigation, common.RouterState],
toggleEventLog: function () { toggleEventLog: function () {
var d = {}; var d = {};
@ -2570,7 +2576,7 @@ var Header = React.createClass({displayName: "Header",
header header
), ),
React.createElement("div", {className: "menu"}, React.createElement("div", {className: "menu"},
React.createElement(this.state.active, {settings: this.props.settings}) React.createElement(this.state.active, null)
) )
) )
); );
@ -2580,7 +2586,7 @@ var Header = React.createClass({displayName: "Header",
module.exports = { module.exports = {
Header: Header Header: Header
} };
},{"../actions.js":2,"../filt/filt.js":20,"../utils.js":24,"./common.js":4,"jquery":"jquery","react":"react"}],15:[function(require,module,exports){ },{"../actions.js":2,"../filt/filt.js":20,"../utils.js":24,"./common.js":4,"jquery":"jquery","react":"react"}],15:[function(require,module,exports){
var React = require("react"); var React = require("react");
@ -2595,22 +2601,40 @@ FlowTable = require("./flowtable.js");
var FlowView = require("./flowview/index.js"); var FlowView = require("./flowview/index.js");
var MainView = React.createClass({displayName: "MainView", var MainView = React.createClass({displayName: "MainView",
mixins: [common.Navigation, common.State], mixins: [common.Navigation, common.RouterState],
contextTypes: {
flowStore: React.PropTypes.object.isRequired,
},
childContextTypes: { childContextTypes: {
returnFocus: React.PropTypes.func.isRequired returnFocus: React.PropTypes.func.isRequired,
view: React.PropTypes.object.isRequired,
}, },
getChildContext: function() { getChildContext: function () {
return { returnFocus: this.returnFocus }; return {
returnFocus: this.returnFocus,
view: this.state.view
};
}, },
returnFocus: function(){ returnFocus: function () {
this.getDOMNode().focus(); this.getDOMNode().focus();
}, },
getInitialState: function () { getInitialState: function () {
var sortKeyFun = false;
var view = new views.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 { return {
flows: [], view: view,
sortKeyFun: false sortKeyFun: sortKeyFun
}; };
}, },
componentWillUnmount: function () {
this.state.view.close();
},
getViewFilt: function () { getViewFilt: function () {
try { try {
var filt = Filt.parse(this.getQuery()[Query.FILTER] || ""); var filt = Filt.parse(this.getQuery()[Query.FILTER] || "");
@ -2629,29 +2653,12 @@ var MainView = React.createClass({displayName: "MainView",
}; };
}, },
componentWillReceiveProps: function (nextProps) { componentWillReceiveProps: function (nextProps) {
if (nextProps.flowStore !== this.props.flowStore) {
this.closeView();
this.openView(nextProps.flowStore);
}
var filterChanged = (this.props.query[Query.FILTER] !== nextProps.query[Query.FILTER]); var filterChanged = (this.props.query[Query.FILTER] !== nextProps.query[Query.FILTER]);
var highlightChanged = (this.props.query[Query.HIGHLIGHT] !== nextProps.query[Query.HIGHLIGHT]); var highlightChanged = (this.props.query[Query.HIGHLIGHT] !== nextProps.query[Query.HIGHLIGHT]);
if (filterChanged || highlightChanged) { if (filterChanged || highlightChanged) {
this.state.view.recalculate(this.getViewFilt(), this.state.sortKeyFun); this.state.view.recalculate(this.getViewFilt(), this.state.sortKeyFun);
} }
}, },
openView: function (store) {
var view = new views.StoreView(store, this.getViewFilt(), this.state.sortKeyFun);
this.setState({
view: view
});
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);
},
onRecalculate: function () { onRecalculate: function () {
this.forceUpdate(); this.forceUpdate();
var selected = this.getSelected(); var selected = this.getSelected();
@ -2670,16 +2677,7 @@ var MainView = React.createClass({displayName: "MainView",
this.selectFlow(flow_to_select); this.selectFlow(flow_to_select);
} }
}, },
closeView: function () { setSortKeyFun: function (sortKeyFun) {
this.state.view.close();
},
componentWillMount: function () {
this.openView(this.props.flowStore);
},
componentWillUnmount: function () {
this.closeView();
},
setSortKeyFun: function(sortKeyFun){
this.setState({ this.setState({
sortKeyFun: sortKeyFun sortKeyFun: sortKeyFun
}); });
@ -2806,7 +2804,7 @@ var MainView = React.createClass({displayName: "MainView",
e.preventDefault(); e.preventDefault();
}, },
getSelected: function () { getSelected: function () {
return this.props.flowStore.get(this.getParams().flowId); return this.context.flowStore.get(this.getParams().flowId);
}, },
render: function () { render: function () {
var selected = this.getSelected(); var selected = this.getSelected();
@ -2824,7 +2822,6 @@ var MainView = React.createClass({displayName: "MainView",
return ( return (
React.createElement("div", {className: "main-view", onKeyDown: this.onKeyDown, tabIndex: "0"}, React.createElement("div", {className: "main-view", onKeyDown: this.onKeyDown, tabIndex: "0"},
React.createElement(FlowTable, {ref: "flowTable", React.createElement(FlowTable, {ref: "flowTable",
view: this.state.view,
selectFlow: this.selectFlow, selectFlow: this.selectFlow,
setSortKeyFun: this.setSortKeyFun, setSortKeyFun: this.setSortKeyFun,
selected: selected}), selected: selected}),
@ -2860,52 +2857,48 @@ var Reports = React.createClass({displayName: "Reports",
var ProxyAppMain = React.createClass({displayName: "ProxyAppMain", var ProxyAppMain = React.createClass({displayName: "ProxyAppMain",
mixins: [common.State], mixins: [common.RouterState],
childContextTypes: {
settingsStore: React.PropTypes.object.isRequired,
flowStore: React.PropTypes.object.isRequired,
eventStore: React.PropTypes.object.isRequired
},
getChildContext: function () {
return {
settingsStore: this.state.settingsStore,
flowStore: this.state.flowStore,
eventStore: this.state.eventStore
};
},
getInitialState: function () { getInitialState: function () {
var eventStore = new store.EventLogStore(); var eventStore = new store.EventLogStore();
var flowStore = new store.FlowStore(); var flowStore = new store.FlowStore();
var settings = new store.SettingsStore(); var settingsStore = new store.SettingsStore();
// Default Settings before fetch // Default Settings before fetch
_.extend(settings.dict,{ _.extend(settingsStore.dict, {});
});
return { return {
settings: settings, settingsStore: settingsStore,
flowStore: flowStore, flowStore: flowStore,
eventStore: eventStore eventStore: eventStore
}; };
}, },
componentDidMount: function () {
this.state.settings.addListener("recalculate", this.onSettingsChange);
window.app = this;
},
componentWillUnmount: function () {
this.state.settings.removeListener("recalculate", this.onSettingsChange);
},
onSettingsChange: function(){
this.setState({
settings: this.state.settings
});
},
render: function () { render: function () {
var eventlog; var eventlog;
if (this.getQuery()[Query.SHOW_EVENTLOG]) { if (this.getQuery()[Query.SHOW_EVENTLOG]) {
eventlog = [ eventlog = [
React.createElement(common.Splitter, {key: "splitter", axis: "y"}), React.createElement(common.Splitter, {key: "splitter", axis: "y"}),
React.createElement(EventLog, {key: "eventlog", eventStore: this.state.eventStore}) React.createElement(EventLog, {key: "eventlog"})
]; ];
} else { } else {
eventlog = null; eventlog = null;
} }
return ( return (
React.createElement("div", {id: "container"}, React.createElement("div", {id: "container"},
React.createElement(header.Header, {settings: this.state.settings.dict}), React.createElement(header.Header, null),
React.createElement(RouteHandler, { React.createElement(RouteHandler, {query: this.getQuery()}),
settings: this.state.settings.dict,
flowStore: this.state.flowStore,
query: this.getQuery()}),
eventlog, eventlog,
React.createElement(Footer, {settings: this.state.settings.dict}) React.createElement(Footer, null)
) )
); );
} }
@ -5203,6 +5196,7 @@ _.extend(StoreView.prototype, EventEmitter.prototype, {
this.store.removeListener("update", this.update); this.store.removeListener("update", this.update);
this.store.removeListener("remove", this.remove); this.store.removeListener("remove", this.remove);
this.store.removeListener("recalculate", this.recalculate); this.store.removeListener("recalculate", this.recalculate);
this.removeAllListeners();
}, },
recalculate: function (filt, sortfun) { recalculate: function (filt, sortfun) {
filt = filt || this.filt || default_filt; filt = filt || this.filt || default_filt;

View File

@ -7,8 +7,8 @@ var AutoScrollMixin = {
componentWillUpdate: function () { componentWillUpdate: function () {
var node = this.getDOMNode(); var node = this.getDOMNode();
this._shouldScrollBottom = ( this._shouldScrollBottom = (
node.scrollTop !== 0 && node.scrollTop !== 0 &&
node.scrollTop + node.clientHeight === node.scrollHeight node.scrollTop + node.clientHeight === node.scrollHeight
); );
}, },
componentDidUpdate: function () { 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 = { var ChildFocus = {
contextTypes: { contextTypes: {
returnFocus: React.PropTypes.func returnFocus: React.PropTypes.func
} }
}; };
var Navigation = _.extend({}, ReactRouter.Navigation, { var Navigation = _.extend({}, ReactRouter.Navigation, {
setQuery: function (dict) { setQuery: function (dict) {
var q = this.context.router.getCurrentQuery(); var q = this.context.router.getCurrentQuery();
for(var i in dict){ for (var i in dict) {
if(dict.hasOwnProperty(i)){ if (dict.hasOwnProperty(i)) {
q[i] = dict[i] || undefined; //falsey values shall be removed. q[i] = dict[i] || undefined; //falsey values shall be removed.
} }
} }
this.replaceWith(this.context.router.getCurrentPath(), this.context.router.getCurrentParams(), q); this.replaceWith(this.context.router.getCurrentPath(), this.context.router.getCurrentParams(), q);
}, },
replaceWith: function(routeNameOrPath, params, query) { replaceWith: function (routeNameOrPath, params, query) {
if(routeNameOrPath === undefined){ if (routeNameOrPath === undefined) {
routeNameOrPath = this.context.router.getCurrentPath(); routeNameOrPath = this.context.router.getCurrentPath();
} }
if(params === undefined){ if (params === undefined) {
params = this.context.router.getCurrentParams(); params = this.context.router.getCurrentParams();
} }
if(query === undefined) { if (query === undefined) {
query = this.context.router.getCurrentQuery(); query = this.context.router.getCurrentQuery();
} }
@ -65,13 +87,13 @@ var Navigation = _.extend({}, ReactRouter.Navigation, {
// react-router is fairly good at changing its API regularly. // 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 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. // we may remove this mixin and access react-router directly again.
var State = _.extend({}, ReactRouter.State, { var RouterState = _.extend({}, ReactRouter.State, {
getQuery: function(){ getQuery: function () {
// For whatever reason, react-router always returns the same object, which makes comparing // 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. // the current props with nextProps impossible. As a workaround, we just clone the query object.
return _.clone(this.context.router.getCurrentQuery()); return _.clone(this.context.router.getCurrentQuery());
}, },
getParams: function(){ getParams: function () {
return _.clone(this.context.router.getCurrentParams()); return _.clone(this.context.router.getCurrentParams());
} }
}); });
@ -183,9 +205,10 @@ var Splitter = React.createClass({
module.exports = { module.exports = {
ChildFocus: ChildFocus, ChildFocus: ChildFocus,
State: State, RouterState: RouterState,
Navigation: Navigation, Navigation: Navigation,
StickyHeadMixin: StickyHeadMixin, StickyHeadMixin: StickyHeadMixin,
AutoScrollMixin: AutoScrollMixin, AutoScrollMixin: AutoScrollMixin,
Splitter: Splitter Splitter: Splitter,
SettingsState: SettingsState
}; };

View File

@ -31,46 +31,37 @@ var LogMessage = React.createClass({
}); });
var EventLogContents = React.createClass({ var EventLogContents = React.createClass({
contextTypes: {
eventStore: React.PropTypes.object.isRequired
},
mixins: [common.AutoScrollMixin, VirtualScrollMixin], mixins: [common.AutoScrollMixin, VirtualScrollMixin],
getInitialState: function () { getInitialState: function () {
return { var filterFn = function (entry) {
log: []
};
},
componentWillMount: function () {
this.openView(this.props.eventStore);
},
componentWillUnmount: function () {
this.closeView();
},
openView: function (store) {
var view = new views.StoreView(store, function (entry) {
return this.props.filter[entry.level]; return this.props.filter[entry.level];
}.bind(this)); };
this.setState({ var view = new views.StoreView(this.context.eventStore, filterFn.bind(this));
view: view
});
view.addListener("add", this.onEventLogChange); view.addListener("add", this.onEventLogChange);
view.addListener("recalculate", this.onEventLogChange); view.addListener("recalculate", this.onEventLogChange);
return {
log: view.list,
view: view
};
}, },
closeView: function () { componentWillUnmount: function () {
this.state.view.close(); this.state.view.close();
}, },
filter: function (entry) {
return this.props.filter[entry.level];
},
onEventLogChange: function () { onEventLogChange: function () {
this.setState({ this.forceUpdate();
log: this.state.view.list
});
}, },
componentWillReceiveProps: function (nextProps) { componentWillReceiveProps: function (nextProps) {
if (nextProps.filter !== this.props.filter) { if (nextProps.filter !== this.props.filter) {
this.props.filter = nextProps.filter; // Dirty: Make sure that view filter sees the update. this.props.filter = nextProps.filter; // Dirty: Make sure that view filter sees the update.
this.state.view.recalculate(); this.state.view.recalculate();
} }
if (nextProps.eventStore !== this.props.eventStore) {
this.closeView();
this.openView(nextProps.eventStore);
}
}, },
getDefaultProps: function () { getDefaultProps: function () {
return { return {
@ -149,7 +140,7 @@ var EventLog = React.createClass({
</div> </div>
</div> </div>
<EventLogContents filter={this.state.filter} eventStore={this.props.eventStore}/> <EventLogContents filter={this.state.filter}/>
</div> </div>
); );
} }

View File

@ -108,33 +108,25 @@ var ROW_HEIGHT = 32;
var FlowTable = React.createClass({ var FlowTable = React.createClass({
mixins: [common.StickyHeadMixin, common.AutoScrollMixin, VirtualScrollMixin], mixins: [common.StickyHeadMixin, common.AutoScrollMixin, VirtualScrollMixin],
contextTypes: {
view: React.PropTypes.object.isRequired
},
getInitialState: function () { getInitialState: function () {
return { return {
columns: flowtable_columns 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 () { 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) { componentWillUnmount: function(){
if (nextProps.view !== this.props.view) { this.context.view.removeListener("add", this.onChange);
if (this.props.view) { this.context.view.removeListener("update", this.onChange);
this.props.view.removeListener("add"); this.context.view.removeListener("remove", this.onChange);
this.props.view.removeListener("update"); this.context.view.removeListener("recalculate", this.onChange);
this.props.view.removeListener("remove");
this.props.view.removeListener("recalculate");
}
this._listen(nextProps.view);
}
}, },
getDefaultProps: function () { getDefaultProps: function () {
return { return {
@ -150,7 +142,7 @@ var FlowTable = React.createClass({
}, },
scrollIntoView: function (flow) { scrollIntoView: function (flow) {
this.scrollRowIntoView( this.scrollRowIntoView(
this.props.view.index(flow), this.context.view.index(flow),
this.refs.body.getDOMNode().offsetTop this.refs.body.getDOMNode().offsetTop
); );
}, },
@ -158,8 +150,8 @@ var FlowTable = React.createClass({
var selected = (flow === this.props.selected); var selected = (flow === this.props.selected);
var highlighted = var highlighted =
( (
this.props.view._highlight && this.context.view._highlight &&
this.props.view._highlight[flow.id] this.context.view._highlight[flow.id]
); );
return <FlowRow key={flow.id} return <FlowRow key={flow.id}
@ -172,9 +164,7 @@ var FlowTable = React.createClass({
/>; />;
}, },
render: function () { render: function () {
//console.log("render flowtable", this.state.start, this.state.stop, this.props.selected); var flows = this.context.view.list;
var flows = this.props.view ? this.props.view.list : [];
var rows = this.renderRows(flows); var rows = this.renderRows(flows);
return ( return (

View File

@ -14,7 +14,7 @@ var allTabs = {
}; };
var FlowView = React.createClass({ var FlowView = React.createClass({
mixins: [common.StickyHeadMixin, common.Navigation, common.State], mixins: [common.StickyHeadMixin, common.Navigation, common.RouterState],
getTabs: function (flow) { getTabs: function (flow) {
var tabs = []; var tabs = [];
["request", "response", "error"].forEach(function (e) { ["request", "response", "error"].forEach(function (e) {

View File

@ -1,9 +1,11 @@
var React = require("react"); var React = require("react");
var common = require("./common.js");
var Footer = React.createClass({ var Footer = React.createClass({
mixins: [common.SettingsState],
render: function () { render: function () {
var mode = this.props.settings.mode; var mode = this.state.settings.mode;
var intercept = this.props.settings.intercept; var intercept = this.state.settings.intercept;
return ( return (
<footer> <footer>
{mode != "regular" ? <span className="label label-success">{mode} mode</span> : null} {mode != "regular" ? <span className="label label-success">{mode} mode</span> : null}

View File

@ -157,7 +157,7 @@ var FilterInput = React.createClass({
}); });
var MainMenu = React.createClass({ var MainMenu = React.createClass({
mixins: [common.Navigation, common.State], mixins: [common.Navigation, common.RouterState, common.SettingsState],
statics: { statics: {
title: "Start", title: "Start",
route: "flows" route: "flows"
@ -178,7 +178,7 @@ var MainMenu = React.createClass({
render: function () { render: function () {
var filter = this.getQuery()[Query.FILTER] || ""; var filter = this.getQuery()[Query.FILTER] || "";
var highlight = this.getQuery()[Query.HIGHLIGHT] || ""; var highlight = this.getQuery()[Query.HIGHLIGHT] || "";
var intercept = this.props.settings.intercept || ""; var intercept = this.state.settings.intercept || "";
return ( return (
<div> <div>
@ -214,7 +214,7 @@ var ViewMenu = React.createClass({
title: "View", title: "View",
route: "flows" route: "flows"
}, },
mixins: [common.Navigation, common.State], mixins: [common.Navigation, common.RouterState],
toggleEventLog: function () { toggleEventLog: function () {
var d = {}; var d = {};
@ -379,7 +379,7 @@ var Header = React.createClass({
{header} {header}
</nav> </nav>
<div className="menu"> <div className="menu">
<this.state.active settings={this.props.settings}/> <this.state.active/>
</div> </div>
</header> </header>
); );
@ -389,4 +389,4 @@ var Header = React.createClass({
module.exports = { module.exports = {
Header: Header Header: Header
} };

View File

@ -10,22 +10,40 @@ FlowTable = require("./flowtable.js");
var FlowView = require("./flowview/index.js"); var FlowView = require("./flowview/index.js");
var MainView = React.createClass({ var MainView = React.createClass({
mixins: [common.Navigation, common.State], mixins: [common.Navigation, common.RouterState],
contextTypes: {
flowStore: React.PropTypes.object.isRequired,
},
childContextTypes: { childContextTypes: {
returnFocus: React.PropTypes.func.isRequired returnFocus: React.PropTypes.func.isRequired,
view: React.PropTypes.object.isRequired,
}, },
getChildContext: function() { getChildContext: function () {
return { returnFocus: this.returnFocus }; return {
returnFocus: this.returnFocus,
view: this.state.view
};
}, },
returnFocus: function(){ returnFocus: function () {
this.getDOMNode().focus(); this.getDOMNode().focus();
}, },
getInitialState: function () { getInitialState: function () {
var sortKeyFun = false;
var view = new views.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 { return {
flows: [], view: view,
sortKeyFun: false sortKeyFun: sortKeyFun
}; };
}, },
componentWillUnmount: function () {
this.state.view.close();
},
getViewFilt: function () { getViewFilt: function () {
try { try {
var filt = Filt.parse(this.getQuery()[Query.FILTER] || ""); var filt = Filt.parse(this.getQuery()[Query.FILTER] || "");
@ -44,29 +62,12 @@ var MainView = React.createClass({
}; };
}, },
componentWillReceiveProps: function (nextProps) { componentWillReceiveProps: function (nextProps) {
if (nextProps.flowStore !== this.props.flowStore) {
this.closeView();
this.openView(nextProps.flowStore);
}
var filterChanged = (this.props.query[Query.FILTER] !== nextProps.query[Query.FILTER]); var filterChanged = (this.props.query[Query.FILTER] !== nextProps.query[Query.FILTER]);
var highlightChanged = (this.props.query[Query.HIGHLIGHT] !== nextProps.query[Query.HIGHLIGHT]); var highlightChanged = (this.props.query[Query.HIGHLIGHT] !== nextProps.query[Query.HIGHLIGHT]);
if (filterChanged || highlightChanged) { if (filterChanged || highlightChanged) {
this.state.view.recalculate(this.getViewFilt(), this.state.sortKeyFun); this.state.view.recalculate(this.getViewFilt(), this.state.sortKeyFun);
} }
}, },
openView: function (store) {
var view = new views.StoreView(store, this.getViewFilt(), this.state.sortKeyFun);
this.setState({
view: view
});
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);
},
onRecalculate: function () { onRecalculate: function () {
this.forceUpdate(); this.forceUpdate();
var selected = this.getSelected(); var selected = this.getSelected();
@ -85,16 +86,7 @@ var MainView = React.createClass({
this.selectFlow(flow_to_select); this.selectFlow(flow_to_select);
} }
}, },
closeView: function () { setSortKeyFun: function (sortKeyFun) {
this.state.view.close();
},
componentWillMount: function () {
this.openView(this.props.flowStore);
},
componentWillUnmount: function () {
this.closeView();
},
setSortKeyFun: function(sortKeyFun){
this.setState({ this.setState({
sortKeyFun: sortKeyFun sortKeyFun: sortKeyFun
}); });
@ -221,7 +213,7 @@ var MainView = React.createClass({
e.preventDefault(); e.preventDefault();
}, },
getSelected: function () { getSelected: function () {
return this.props.flowStore.get(this.getParams().flowId); return this.context.flowStore.get(this.getParams().flowId);
}, },
render: function () { render: function () {
var selected = this.getSelected(); var selected = this.getSelected();
@ -239,7 +231,6 @@ var MainView = React.createClass({
return ( return (
<div className="main-view" onKeyDown={this.onKeyDown} tabIndex="0"> <div className="main-view" onKeyDown={this.onKeyDown} tabIndex="0">
<FlowTable ref="flowTable" <FlowTable ref="flowTable"
view={this.state.view}
selectFlow={this.selectFlow} selectFlow={this.selectFlow}
setSortKeyFun={this.setSortKeyFun} setSortKeyFun={this.setSortKeyFun}
selected={selected} /> selected={selected} />

View File

@ -20,52 +20,48 @@ var Reports = React.createClass({
var ProxyAppMain = React.createClass({ var ProxyAppMain = React.createClass({
mixins: [common.State], mixins: [common.RouterState],
childContextTypes: {
settingsStore: React.PropTypes.object.isRequired,
flowStore: React.PropTypes.object.isRequired,
eventStore: React.PropTypes.object.isRequired
},
getChildContext: function () {
return {
settingsStore: this.state.settingsStore,
flowStore: this.state.flowStore,
eventStore: this.state.eventStore
};
},
getInitialState: function () { getInitialState: function () {
var eventStore = new store.EventLogStore(); var eventStore = new store.EventLogStore();
var flowStore = new store.FlowStore(); var flowStore = new store.FlowStore();
var settings = new store.SettingsStore(); var settingsStore = new store.SettingsStore();
// Default Settings before fetch // Default Settings before fetch
_.extend(settings.dict,{ _.extend(settingsStore.dict, {});
});
return { return {
settings: settings, settingsStore: settingsStore,
flowStore: flowStore, flowStore: flowStore,
eventStore: eventStore eventStore: eventStore
}; };
}, },
componentDidMount: function () {
this.state.settings.addListener("recalculate", this.onSettingsChange);
window.app = this;
},
componentWillUnmount: function () {
this.state.settings.removeListener("recalculate", this.onSettingsChange);
},
onSettingsChange: function(){
this.setState({
settings: this.state.settings
});
},
render: function () { render: function () {
var eventlog; var eventlog;
if (this.getQuery()[Query.SHOW_EVENTLOG]) { if (this.getQuery()[Query.SHOW_EVENTLOG]) {
eventlog = [ eventlog = [
<common.Splitter key="splitter" axis="y"/>, <common.Splitter key="splitter" axis="y"/>,
<EventLog key="eventlog" eventStore={this.state.eventStore}/> <EventLog key="eventlog"/>
]; ];
} else { } else {
eventlog = null; eventlog = null;
} }
return ( return (
<div id="container"> <div id="container">
<header.Header settings={this.state.settings.dict}/> <header.Header/>
<RouteHandler <RouteHandler query={this.getQuery()}/>
settings={this.state.settings.dict}
flowStore={this.state.flowStore}
query={this.getQuery()}/>
{eventlog} {eventlog}
<Footer settings={this.state.settings.dict}/> <Footer/>
</div> </div>
); );
} }

View File

@ -35,6 +35,7 @@ _.extend(StoreView.prototype, EventEmitter.prototype, {
this.store.removeListener("update", this.update); this.store.removeListener("update", this.update);
this.store.removeListener("remove", this.remove); this.store.removeListener("remove", this.remove);
this.store.removeListener("recalculate", this.recalculate); this.store.removeListener("recalculate", this.recalculate);
this.removeAllListeners();
}, },
recalculate: function (filt, sortfun) { recalculate: function (filt, sortfun) {
filt = filt || this.filt || default_filt; filt = filt || this.filt || default_filt;