aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaximilian Hils <git@maximilianhils.com>2016-06-03 18:38:03 -0700
committerMaximilian Hils <git@maximilianhils.com>2016-06-03 18:38:25 -0700
commit1b327f34c30e490e6f26ec1ee4e6cfe7b40a47eb (patch)
tree2bd7c0a01e4b6a9104162c164b7c156bf1f41ebb
parentff9cc8b28393f6beba1ed42ade074672183313e9 (diff)
downloadmitmproxy-1b327f34c30e490e6f26ec1ee4e6cfe7b40a47eb.tar.gz
mitmproxy-1b327f34c30e490e6f26ec1ee4e6cfe7b40a47eb.tar.bz2
mitmproxy-1b327f34c30e490e6f26ec1ee4e6cfe7b40a47eb.zip
web: add redux list views
-rw-r--r--mitmproxy/web/static/app.js194
-rw-r--r--web/src/js/ducks/eventLog.js16
-rw-r--r--web/src/js/ducks/utils/list.js6
-rw-r--r--web/src/js/ducks/utils/view.js58
4 files changed, 191 insertions, 83 deletions
diff --git a/mitmproxy/web/static/app.js b/mitmproxy/web/static/app.js
index 83fd0c58..a485bfca 100644
--- a/mitmproxy/web/static/app.js
+++ b/mitmproxy/web/static/app.js
@@ -304,7 +304,7 @@ function isUndefined(arg) {
Object.defineProperty(exports, "__esModule", {
value: true
});
-exports.Query = exports.FlowActions = exports.EventLogActions = exports.SettingsActions = exports.ConnectionActions = exports.StoreCmds = exports.ActionTypes = undefined;
+exports.Query = exports.FlowActions = exports.SettingsActions = exports.ConnectionActions = exports.StoreCmds = exports.ActionTypes = undefined;
var _jquery = require("jquery");
@@ -374,21 +374,6 @@ var SettingsActions = exports.SettingsActions = {
}
};
-var EventLogActions_event_id = 0;
-var EventLogActions = exports.EventLogActions = {
- add_event: function add_event(message) {
- _dispatcher.AppDispatcher.dispatchViewAction({
- type: ActionTypes.EVENT_STORE,
- cmd: StoreCmds.ADD,
- data: {
- message: message,
- level: "web",
- id: "viewAction-" + EventLogActions_event_id++
- }
- });
- }
-};
-
var FlowActions = exports.FlowActions = {
accept: function accept(flow) {
_jquery2.default.post("/flows/" + flow.id + "/accept");
@@ -452,7 +437,7 @@ var Query = exports.Query = {
SHOW_EVENTLOG: "e"
};
-},{"./dispatcher.js":22,"./utils.js":31,"jquery":"jquery"}],3:[function(require,module,exports){
+},{"./dispatcher.js":22,"./utils.js":32,"jquery":"jquery"}],3:[function(require,module,exports){
'use strict';
var _react = require('react');
@@ -724,7 +709,7 @@ ToggleInputButton.propTypes = {
onToggleChanged: _react2.default.PropTypes.func.isRequired
};
-},{"../utils.js":31,"lodash":"lodash","react":"react","react-dom":"react-dom"}],5:[function(require,module,exports){
+},{"../utils.js":32,"lodash":"lodash","react":"react","react-dom":"react-dom"}],5:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -982,7 +967,7 @@ var ValueEditor = exports.ValueEditor = _react2.default.createClass({
}
});
-},{"../utils.js":31,"react":"react","react-dom":"react-dom"}],6:[function(require,module,exports){
+},{"../utils.js":32,"react":"react","react-dom":"react-dom"}],6:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -1477,7 +1462,7 @@ var all_columns = [TLSColumn, IconColumn, PathColumn, MethodColumn, StatusColumn
exports.default = all_columns;
-},{"../flow/utils.js":28,"../utils.js":31,"react":"react"}],8:[function(require,module,exports){
+},{"../flow/utils.js":29,"../utils.js":32,"react":"react"}],8:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -1769,7 +1754,7 @@ FlowTable.defaultProps = {
};
exports.default = (0, _AutoScroll2.default)(FlowTable);
-},{"../utils.js":31,"./flowtable-columns.js":7,"./helpers/AutoScroll":16,"./helpers/VirtualScroll":17,"classnames":"classnames","lodash":"lodash","react":"react","react-dom":"react-dom","shallowequal":"shallowequal"}],9:[function(require,module,exports){
+},{"../utils.js":32,"./flowtable-columns.js":7,"./helpers/AutoScroll":16,"./helpers/VirtualScroll":17,"classnames":"classnames","lodash":"lodash","react":"react","react-dom":"react-dom","shallowequal":"shallowequal"}],9:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -2123,7 +2108,7 @@ var ContentView = _react2.default.createClass({
exports.default = ContentView;
-},{"../../flow/utils.js":28,"../../utils.js":31,"lodash":"lodash","react":"react"}],10:[function(require,module,exports){
+},{"../../flow/utils.js":29,"../../utils.js":32,"lodash":"lodash","react":"react"}],10:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -2391,7 +2376,7 @@ var Details = _react2.default.createClass({
exports.default = Details;
-},{"../../utils.js":31,"lodash":"lodash","react":"react"}],11:[function(require,module,exports){
+},{"../../utils.js":32,"lodash":"lodash","react":"react"}],11:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -2885,7 +2870,7 @@ var Error = exports.Error = _react2.default.createClass({
}
});
-},{"../../actions.js":2,"../../flow/utils.js":28,"../../utils.js":31,"../editor.js":5,"./contentview.js":9,"lodash":"lodash","react":"react","react-dom":"react-dom"}],13:[function(require,module,exports){
+},{"../../actions.js":2,"../../flow/utils.js":29,"../../utils.js":32,"../editor.js":5,"./contentview.js":9,"lodash":"lodash","react":"react","react-dom":"react-dom"}],13:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -3068,7 +3053,7 @@ function Footer(_ref) {
);
}
-},{"../utils.js":31,"./common.js":4,"react":"react"}],15:[function(require,module,exports){
+},{"../utils.js":32,"./common.js":4,"react":"react"}],15:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -3643,7 +3628,7 @@ var Header = exports.Header = _react2.default.createClass({
}
});
-},{"../actions.js":2,"../filt/filt.js":27,"../utils.js":31,"./common.js":4,"./eventlog":6,"jquery":"jquery","react":"react","react-dom":"react-dom","react-redux":"react-redux"}],16:[function(require,module,exports){
+},{"../actions.js":2,"../filt/filt.js":28,"../utils.js":32,"./common.js":4,"./eventlog":6,"jquery":"jquery","react":"react","react-dom":"react-dom","react-redux":"react-redux"}],16:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -4059,7 +4044,7 @@ var MainView = _react2.default.createClass({
exports.default = MainView;
-},{"../actions.js":2,"../filt/filt.js":27,"../store/view.js":30,"../utils.js":31,"./common.js":4,"./flowtable.js":8,"./flowview/index.js":11,"react":"react"}],19:[function(require,module,exports){
+},{"../actions.js":2,"../filt/filt.js":28,"../store/view.js":31,"../utils.js":32,"./common.js":4,"./flowtable.js":8,"./flowview/index.js":11,"react":"react"}],19:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -4195,7 +4180,7 @@ var Prompt = _react2.default.createClass({
exports.default = Prompt;
-},{"../utils.js":31,"lodash":"lodash","react":"react","react-dom":"react-dom"}],20:[function(require,module,exports){
+},{"../utils.js":32,"lodash":"lodash","react":"react","react-dom":"react-dom"}],20:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -4259,7 +4244,6 @@ var ProxyAppMain = _react2.default.createClass({
childContextTypes: {
flowStore: _react2.default.PropTypes.object.isRequired,
- eventStore: _react2.default.PropTypes.object.isRequired,
returnFocus: _react2.default.PropTypes.func.isRequired,
location: _react2.default.PropTypes.object.isRequired
},
@@ -4298,13 +4282,11 @@ var ProxyAppMain = _react2.default.createClass({
getChildContext: function getChildContext() {
return {
flowStore: this.state.flowStore,
- eventStore: this.state.eventStore,
returnFocus: this.focus,
location: this.props.location
};
},
getInitialState: function getInitialState() {
- var eventStore = new _store.EventLogStore();
var flowStore = new _store.FlowStore();
var settingsStore = new _store.SettingsStore();
@@ -4313,8 +4295,7 @@ var ProxyAppMain = _react2.default.createClass({
_lodash2.default.extend(settingsStore.dict, {});
return {
settings: settingsStore.dict,
- flowStore: flowStore,
- eventStore: eventStore
+ flowStore: flowStore
};
},
focus: function focus() {
@@ -4391,7 +4372,7 @@ var App = exports.App = _react2.default.createElement(
)
);
-},{"../store/store.js":29,"../utils.js":31,"./common.js":4,"./eventlog.js":6,"./footer.js":14,"./header.js":15,"./mainview.js":18,"lodash":"lodash","react":"react","react-dom":"react-dom","react-redux":"react-redux","react-router":"react-router"}],21:[function(require,module,exports){
+},{"../store/store.js":30,"../utils.js":32,"./common.js":4,"./eventlog.js":6,"./footer.js":14,"./header.js":15,"./mainview.js":18,"lodash":"lodash","react":"react","react-dom":"react-dom","react-redux":"react-redux","react-router":"react-router"}],21:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -4437,17 +4418,17 @@ function Connection(url, dispatch) {
};
ws.onerror = function () {
_actions.ConnectionActions.error();
- _actions.EventLogActions.add_event("WebSocket connection error.");
+ dispatch(eventLogActions.addLogEntry("WebSocket connection error."));
};
ws.onclose = function () {
_actions.ConnectionActions.close();
- _actions.EventLogActions.add_event("WebSocket connection closed.");
- dispatch(websocketActions.disconnected());
+ dispatch(eventLogActions.addLogEntry("WebSocket connection closed."));
+ dispatch(webSocketActions.disconnected());
};
return ws;
}
-},{"./actions.js":2,"./dispatcher.js":22,"./ducks/eventLog":23,"./ducks/websocket":26}],22:[function(require,module,exports){
+},{"./actions.js":2,"./dispatcher.js":22,"./ducks/eventLog":23,"./ducks/websocket":27}],22:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -4477,7 +4458,7 @@ AppDispatcher.dispatchServerAction = function (action) {
};
},{"flux":"flux"}],23:[function(require,module,exports){
-'use strict';
+"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
@@ -4491,10 +4472,12 @@ exports.toggleEventLogFilter = toggleEventLogFilter;
exports.toggleEventLogVisibility = toggleEventLogVisibility;
exports.addLogEntry = addLogEntry;
-var _list = require('./utils/list');
+var _list = require("./utils/list");
var _list2 = _interopRequireDefault(_list);
+var _view = require("./utils/view");
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
@@ -4509,8 +4492,6 @@ var reduceList = _makeList.reduceList;
var addToList = _makeList.addToList;
var updateList = _makeList.updateList;
var fetchList = _makeList.fetchList;
-exports.updateLogEntries = updateList;
-exports.fetchLogEntries = fetchList;
var defaultState = {
@@ -4533,7 +4514,7 @@ function reducer() {
var filter = _extends({}, state.filter, _defineProperty({}, action.filter, !state.filter[action.filter]));
return _extends({}, state, {
filter: filter,
- filteredEvents: state.events.list.filter(function (x) {
+ filteredEvents: (0, _view.updateViewFilter)(state.events.list, function (x) {
return filter[x.level];
})
});
@@ -4545,7 +4526,7 @@ function reducer() {
var events = reduceList(state.events, action);
return _extends({}, state, {
events: events,
- filteredEvents: events.list.filter(function (x) {
+ filteredEvents: (0, _view.updateViewList)(state.filteredEvents, events, action, function (x) {
return state.filter[x.level];
})
});
@@ -4567,11 +4548,13 @@ function addLogEntry(message) {
return addToList({
message: message,
level: level,
- id: 'log-' + id++
+ id: "log-" + id++
});
}
+exports.updateLogEntries = updateList;
+exports.fetchLogEntries = fetchList;
-},{"./utils/list":25}],24:[function(require,module,exports){
+},{"./utils/list":25,"./utils/view":26}],24:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@@ -4597,12 +4580,13 @@ var rootReducer = (0, _redux.combineReducers)({
exports.default = rootReducer;
-},{"./eventLog.js":23,"./websocket.js":26,"redux":"redux"}],25:[function(require,module,exports){
+},{"./eventLog.js":23,"./websocket.js":27,"redux":"redux"}],25:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
+exports.RECEIVE_LIST = exports.REQUEST_LIST = exports.ADD = undefined;
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
@@ -4614,9 +4598,9 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
-var ADD = "ADD";
-var REQUEST_LIST = "REQUEST_LIST";
-var RECEIVE_LIST = "RECEIVE_LIST";
+var ADD = exports.ADD = "ADD";
+var REQUEST_LIST = exports.REQUEST_LIST = "REQUEST_LIST";
+var RECEIVE_LIST = exports.RECEIVE_LIST = "RECEIVE_LIST";
var defaultState = {
list: [],
@@ -4754,7 +4738,84 @@ function makeList(actionType, fetchURL) {
return { reduceList: reduceList, addToList: addToList, updateList: updateList, fetchList: fetchList };
}
-},{"../../utils":31}],26:[function(require,module,exports){
+},{"../../utils":32}],26:[function(require,module,exports){
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.updateViewList = updateViewList;
+exports.updateViewFilter = updateViewFilter;
+
+var _list = require("./list");
+
+function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
+
+var defaultFilterFn = function defaultFilterFn(x) {
+ return true;
+};
+var defaultSortFn = false;
+
+var makeCompareFn = function makeCompareFn(sortFn) {
+ var compareFn = function compareFn(a, b) {
+ var akey = sortFn(a),
+ bkey = sortFn(b);
+ if (akey < bkey) {
+ return -1;
+ } else if (akey > bkey) {
+ return 1;
+ } else {
+ return 0;
+ }
+ };
+ if (sortFn.reverse) return function (a, b) {
+ return compareFn(b, a);
+ };
+ return compareFn;
+};
+
+var sortedInsert = function sortedInsert(list, sortFn, item) {
+ var l = [].concat(_toConsumableArray(list), [item]);
+ var compareFn = makeCompareFn(sortFn);
+
+ // only sort if sorting order is not correct yet
+ if (sortFn && compareFn(list[list.length - 1], item) > 0) {
+ // TODO: This is untested
+ console.debug("sorting view...");
+ l.sort(compareFn);
+ }
+ return l;
+};
+
+// for when the list changes
+function updateViewList(state, nextList, action) {
+ var filterFn = arguments.length <= 3 || arguments[3] === undefined ? defaultFilterFn : arguments[3];
+ var sortFn = arguments.length <= 4 || arguments[4] === undefined ? defaultSortFn : arguments[4];
+
+ switch (action.cmd) {
+ case _list.REQUEST_LIST:
+ return state;
+ case _list.RECEIVE_LIST:
+ return updateViewFilter(nextList.list, filterFn, sortFn);
+ case _list.ADD:
+ if (filterFn(action.item)) return sortedInsert(state, sortFn, action.item);
+ return state;
+ default:
+ console.error("Unknown list action: ", action);
+ return state;
+ }
+}
+
+function updateViewFilter(list) {
+ var filterFn = arguments.length <= 1 || arguments[1] === undefined ? defaultFilterFn : arguments[1];
+ var sortFn = arguments.length <= 2 || arguments[2] === undefined ? defaultSortFn : arguments[2];
+
+ var filtered = list.filter(filterFn);
+ if (sortFn) filtered.sort(makeCompareFn(sortFn));
+ return filtered;
+}
+
+},{"./list":25}],27:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@@ -4795,7 +4856,7 @@ function disconnected() {
return { type: DISCONNECTED };
}
-},{}],27:[function(require,module,exports){
+},{}],28:[function(require,module,exports){
"use strict";
module.exports = function () {
@@ -6699,7 +6760,7 @@ module.exports = function () {
};
}();
-},{"../flow/utils.js":28}],28:[function(require,module,exports){
+},{"../flow/utils.js":29}],29:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -6833,7 +6894,7 @@ var parseHttpVersion = exports.parseHttpVersion = function parseHttpVersion(http
});
};
-},{"jquery":"jquery","lodash":"lodash"}],29:[function(require,module,exports){
+},{"jquery":"jquery","lodash":"lodash"}],30:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -6841,7 +6902,6 @@ Object.defineProperty(exports, "__esModule", {
});
exports.FlowStore = FlowStore;
exports.SettingsStore = SettingsStore;
-exports.EventLogStore = EventLogStore;
var _lodash = require("lodash");
@@ -6967,7 +7027,7 @@ _lodash2.default.extend(LiveStoreMixin.prototype, {
this._fetchxhr = _jquery2.default.getJSON("/" + this.type).done(function (message) {
this.handle_fetch(message.data);
}.bind(this)).fail(function () {
- _actions.EventLogActions.add_event("Could not fetch " + this.type);
+ console.error("Could not fetch " + this.type);
}.bind(this));
}
},
@@ -7003,24 +7063,7 @@ function SettingsStore() {
return new LiveDictStore(_actions.ActionTypes.SETTINGS_STORE);
}
-function EventLogStore() {
- LiveListStore.call(this, _actions.ActionTypes.EVENT_STORE);
-}
-_lodash2.default.extend(EventLogStore.prototype, LiveListStore.prototype, {
- fetch: function fetch() {
- LiveListStore.prototype.fetch.apply(this, arguments);
-
- // Make sure to display updates even if fetching all events failed.
- // This way, we can send "fetch failed" log messages to the log.
- if (this._fetchxhr) {
- this._fetchxhr.fail(function () {
- this.handle_fetch(null);
- }.bind(this));
- }
- }
-});
-
-},{"../actions.js":2,"../dispatcher.js":22,"events":1,"jquery":"jquery","lodash":"lodash"}],30:[function(require,module,exports){
+},{"../actions.js":2,"../dispatcher.js":22,"events":1,"jquery":"jquery","lodash":"lodash"}],31:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -7150,7 +7193,7 @@ _lodash2.default.extend(StoreView.prototype, _events.EventEmitter.prototype, {
}
});
-},{"../utils.js":31,"events":1,"lodash":"lodash"}],31:[function(require,module,exports){
+},{"../utils.js":32,"events":1,"lodash":"lodash"}],32:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -7265,7 +7308,6 @@ _jquery2.default.ajaxPrefilter(function (options) {
}
var message = jqXHR.responseText;
console.error(thrownError, message, arguments);
- _actions2.default.EventLogActions.add_event(thrownError + ": " + message);
alert(message);
});
diff --git a/web/src/js/ducks/eventLog.js b/web/src/js/ducks/eventLog.js
index 081a2276..883f537c 100644
--- a/web/src/js/ducks/eventLog.js
+++ b/web/src/js/ducks/eventLog.js
@@ -1,4 +1,5 @@
import makeList, {ADD} from "./utils/list"
+import {updateViewFilter, updateViewList} from "./utils/view"
const TOGGLE_FILTER = 'TOGGLE_EVENTLOG_FILTER'
const TOGGLE_VISIBILITY = 'TOGGLE_EVENTLOG_VISIBILITY'
@@ -11,7 +12,6 @@ const {
fetchList,
} = makeList(UPDATE_LOG, "/events");
-export {updateList as updateLogEntries, fetchList as fetchLogEntries}
const defaultState = {
visible: false,
@@ -34,7 +34,10 @@ export default function reducer(state = defaultState, action) {
return {
...state,
filter,
- filteredEvents: state.events.list.filter(x => filter[x.level])
+ filteredEvents: updateViewFilter(
+ state.events.list,
+ x => filter[x.level]
+ )
}
case TOGGLE_VISIBILITY:
return {
@@ -46,7 +49,11 @@ export default function reducer(state = defaultState, action) {
return {
...state,
events,
- filteredEvents: events.list.filter(x => state.filter[x.level])
+ filteredEvents: updateViewList(
+ state.filteredEvents,
+ events, action,
+ x => state.filter[x.level]
+ )
}
default:
return state
@@ -67,4 +74,5 @@ export function addLogEntry(message, level = "web") {
level,
id: `log-${id++}`
})
-} \ No newline at end of file
+}
+export {updateList as updateLogEntries, fetchList as fetchLogEntries} \ No newline at end of file
diff --git a/web/src/js/ducks/utils/list.js b/web/src/js/ducks/utils/list.js
index 37b2ae3a..b750982e 100644
--- a/web/src/js/ducks/utils/list.js
+++ b/web/src/js/ducks/utils/list.js
@@ -1,8 +1,8 @@
import {fetchApi} from "../../utils";
-const ADD = "ADD"
-const REQUEST_LIST = "REQUEST_LIST"
-const RECEIVE_LIST = "RECEIVE_LIST"
+export const ADD = "ADD"
+export const REQUEST_LIST = "REQUEST_LIST"
+export const RECEIVE_LIST = "RECEIVE_LIST"
const defaultState = {
diff --git a/web/src/js/ducks/utils/view.js b/web/src/js/ducks/utils/view.js
new file mode 100644
index 00000000..4b265f89
--- /dev/null
+++ b/web/src/js/ducks/utils/view.js
@@ -0,0 +1,58 @@
+import {ADD, REQUEST_LIST, RECEIVE_LIST} from "./list"
+
+const defaultFilterFn = x => true
+const defaultSortFn = false
+
+const makeCompareFn = sortFn => {
+ let compareFn = (a, b) => {
+ let akey = sortFn(a),
+ bkey = sortFn(b);
+ if (akey < bkey) {
+ return -1;
+ } else if (akey > bkey) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ if (sortFn.reverse)
+ return (a, b) => compareFn(b, a)
+ return compareFn
+}
+
+const sortedInsert = (list, sortFn, item) => {
+ let l = [...list, item]
+ let compareFn = makeCompareFn(sortFn)
+
+ // only sort if sorting order is not correct yet
+ if (sortFn && compareFn(list[list.length - 1], item) > 0) {
+ // TODO: This is untested
+ console.debug("sorting view...")
+ l.sort(compareFn)
+ }
+ return l
+}
+
+// for when the list changes
+export function updateViewList(state, nextList, action, filterFn = defaultFilterFn, sortFn = defaultSortFn) {
+ switch (action.cmd) {
+ case REQUEST_LIST:
+ return state
+ case RECEIVE_LIST:
+ return updateViewFilter(nextList.list, filterFn, sortFn)
+ case ADD:
+ if (filterFn(action.item))
+ return sortedInsert(state, sortFn, action.item)
+ return state
+ default:
+ console.error("Unknown list action: ", action);
+ return state
+ }
+}
+
+export function updateViewFilter(list, filterFn = defaultFilterFn, sortFn = defaultSortFn) {
+ let filtered = list.filter(filterFn)
+ if (sortFn)
+ filtered.sort(makeCompareFn(sortFn))
+ return filtered
+} \ No newline at end of file