aboutsummaryrefslogtreecommitdiff
path: root/frontend
diff options
context:
space:
mode:
authorEmiliano Ciavatta2020-10-15 07:54:25 +0000
committerEmiliano Ciavatta2020-10-15 07:54:25 +0000
commit97b0ee38fcf1e78e66dfe2a2816c95c3c3b10705 (patch)
treeed36b83d39163d872e55d297caada23092b7fcd4 /frontend
parent08456e7f2e1c1af6fc8fdbf580c0178a25b93f8b (diff)
Update filters
Diffstat (limited to 'frontend')
-rw-r--r--frontend/.eslintrc.js216
-rw-r--r--frontend/src/components/Header.js47
-rw-r--r--frontend/src/components/Header.scss9
-rw-r--r--frontend/src/components/dialogs/Filters.js108
-rw-r--r--frontend/src/components/dialogs/Filters.scss5
-rw-r--r--frontend/src/components/fields/ButtonField.js3
-rw-r--r--frontend/src/components/fields/ButtonField.scss7
-rw-r--r--frontend/src/components/filters/AdvancedFilters.js54
-rw-r--r--frontend/src/components/filters/BooleanConnectionsFilter.js2
-rw-r--r--frontend/src/components/filters/FiltersDefinitions.js73
-rw-r--r--frontend/src/components/filters/StringConnectionsFilter.js2
-rw-r--r--frontend/src/components/panels/ConnectionsPane.js22
-rw-r--r--frontend/src/dispatcher.js2
-rw-r--r--frontend/src/index.js6
-rw-r--r--frontend/src/utils.js16
15 files changed, 158 insertions, 414 deletions
diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js
deleted file mode 100644
index 16ff228..0000000
--- a/frontend/.eslintrc.js
+++ /dev/null
@@ -1,216 +0,0 @@
-const OFF = 0, WARN = 1, ERROR = 2;
-
-module.exports = exports = {
- "env": {
- "es6": true
- },
-
- "ecmaFeatures": {
- // env=es6 doesn't include modules, which we are using
- "modules": true
- },
-
- "extends": "eslint:recommended",
-
- "rules": {
- // Possible Errors (overrides from recommended set)
- "no-extra-parens": ERROR,
- "no-unexpected-multiline": ERROR,
- // All JSDoc comments must be valid
- "valid-jsdoc": [ ERROR, {
- "requireReturn": false,
- "requireReturnDescription": false,
- "requireParamDescription": true,
- "prefer": {
- "return": "returns"
- }
- }],
-
- // Best Practices
-
- // Allowed a getter without setter, but all setters require getters
- "accessor-pairs": [ ERROR, {
- "getWithoutSet": false,
- "setWithoutGet": true
- }],
- "block-scoped-var": WARN,
- "consistent-return": ERROR,
- "curly": ERROR,
- "default-case": WARN,
- // the dot goes with the property when doing multiline
- "dot-location": [ WARN, "property" ],
- "dot-notation": WARN,
- "eqeqeq": [ ERROR, "smart" ],
- "guard-for-in": WARN,
- "no-alert": ERROR,
- "no-caller": ERROR,
- "no-case-declarations": WARN,
- "no-div-regex": WARN,
- "no-else-return": WARN,
- "no-empty-label": WARN,
- "no-empty-pattern": WARN,
- "no-eq-null": WARN,
- "no-eval": ERROR,
- "no-extend-native": ERROR,
- "no-extra-bind": WARN,
- "no-floating-decimal": WARN,
- "no-implicit-coercion": [ WARN, {
- "boolean": true,
- "number": true,
- "string": true
- }],
- "no-implied-eval": ERROR,
- "no-invalid-this": ERROR,
- "no-iterator": ERROR,
- "no-labels": WARN,
- "no-lone-blocks": WARN,
- "no-loop-func": ERROR,
- "no-magic-numbers": WARN,
- "no-multi-spaces": ERROR,
- "no-multi-str": WARN,
- "no-native-reassign": ERROR,
- "no-new-func": ERROR,
- "no-new-wrappers": ERROR,
- "no-new": ERROR,
- "no-octal-escape": ERROR,
- "no-param-reassign": ERROR,
- "no-process-env": WARN,
- "no-proto": ERROR,
- "no-redeclare": ERROR,
- "no-return-assign": ERROR,
- "no-script-url": ERROR,
- "no-self-compare": ERROR,
- "no-throw-literal": ERROR,
- "no-unused-expressions": ERROR,
- "no-useless-call": ERROR,
- "no-useless-concat": ERROR,
- "no-void": WARN,
- // Produce warnings when something is commented as TODO or FIXME
- "no-warning-comments": [ WARN, {
- "terms": [ "TODO", "FIXME" ],
- "location": "start"
- }],
- "no-with": WARN,
- "radix": WARN,
- "vars-on-top": ERROR,
- // Enforces the style of wrapped functions
- "wrap-iife": [ ERROR, "outside" ],
- "yoda": ERROR,
-
- // Strict Mode - for ES6, never use strict.
- "strict": [ ERROR, "never" ],
-
- // Variables
- "init-declarations": [ ERROR, "always" ],
- "no-catch-shadow": WARN,
- "no-delete-var": ERROR,
- "no-label-var": ERROR,
- "no-shadow-restricted-names": ERROR,
- "no-shadow": WARN,
- // We require all vars to be initialized (see init-declarations)
- // If we NEED a var to be initialized to undefined, it needs to be explicit
- "no-undef-init": OFF,
- "no-undef": ERROR,
- "no-undefined": OFF,
- "no-unused-vars": WARN,
- // Disallow hoisting - let & const don't allow hoisting anyhow
- "no-use-before-define": ERROR,
-
- // Node.js and CommonJS
- "callback-return": [ WARN, [ "callback", "next" ]],
- "global-require": ERROR,
- "handle-callback-err": WARN,
- "no-mixed-requires": WARN,
- "no-new-require": ERROR,
- // Use path.concat instead
- "no-path-concat": ERROR,
- "no-process-exit": ERROR,
- "no-restricted-modules": OFF,
- "no-sync": WARN,
-
- // ECMAScript 6 support
- "arrow-body-style": [ ERROR, "always" ],
- "arrow-parens": [ ERROR, "always" ],
- "arrow-spacing": [ ERROR, { "before": true, "after": true }],
- "constructor-super": ERROR,
- "generator-star-spacing": [ ERROR, "before" ],
- "no-arrow-condition": ERROR,
- "no-class-assign": ERROR,
- "no-const-assign": ERROR,
- "no-dupe-class-members": ERROR,
- "no-this-before-super": ERROR,
- "no-var": WARN,
- "object-shorthand": [ WARN, "never" ],
- "prefer-arrow-callback": WARN,
- "prefer-spread": WARN,
- "prefer-template": WARN,
- "require-yield": ERROR,
-
- // Stylistic - everything here is a warning because of style.
- "array-bracket-spacing": [ WARN, "always" ],
- "block-spacing": [ WARN, "always" ],
- "brace-style": [ WARN, "1tbs", { "allowSingleLine": false } ],
- "camelcase": WARN,
- "comma-spacing": [ WARN, { "before": false, "after": true } ],
- "comma-style": [ WARN, "last" ],
- "computed-property-spacing": [ WARN, "never" ],
- "consistent-this": [ WARN, "self" ],
- "eol-last": WARN,
- "func-names": WARN,
- "func-style": [ WARN, "declaration" ],
- "id-length": [ WARN, { "min": 2, "max": 32 } ],
- "indent": [ WARN, 4 ],
- "jsx-quotes": [ WARN, "prefer-double" ],
- "linebreak-style": [ WARN, "unix" ],
- "lines-around-comment": [ WARN, { "beforeBlockComment": true } ],
- "max-depth": [ WARN, 8 ],
- "max-len": [ WARN, 132 ],
- "max-nested-callbacks": [ WARN, 8 ],
- "max-params": [ WARN, 8 ],
- "new-cap": WARN,
- "new-parens": WARN,
- "no-array-constructor": WARN,
- "no-bitwise": OFF,
- "no-continue": OFF,
- "no-inline-comments": OFF,
- "no-lonely-if": WARN,
- "no-mixed-spaces-and-tabs": WARN,
- "no-multiple-empty-lines": WARN,
- "no-negated-condition": OFF,
- "no-nested-ternary": WARN,
- "no-new-object": WARN,
- "no-plusplus": OFF,
- "no-spaced-func": WARN,
- "no-ternary": OFF,
- "no-trailing-spaces": WARN,
- "no-underscore-dangle": WARN,
- "no-unneeded-ternary": WARN,
- "object-curly-spacing": [ WARN, "always" ],
- "one-var": OFF,
- "operator-assignment": [ WARN, "never" ],
- "operator-linebreak": [ WARN, "after" ],
- "padded-blocks": [ WARN, "never" ],
- "quote-props": [ WARN, "consistent-as-needed" ],
- "quotes": [ WARN, "single" ],
- "require-jsdoc": [ WARN, {
- "require": {
- "FunctionDeclaration": true,
- "MethodDefinition": true,
- "ClassDeclaration": false
- }
- }],
- "semi-spacing": [ WARN, { "before": false, "after": true }],
- "semi": [ ERROR, "always" ],
- "sort-vars": OFF,
- "space-after-keywords": [ WARN, "always" ],
- "space-before-blocks": [ WARN, "always" ],
- "space-before-function-paren": [ WARN, "never" ],
- "space-before-keywords": [ WARN, "always" ],
- "space-in-parens": [ WARN, "never" ],
- "space-infix-ops": [ WARN, { "int32Hint": true } ],
- "space-return-throw-case": ERROR,
- "space-unary-ops": ERROR,
- "spaced-comment": [ WARN, "always" ],
- "wrap-regex": WARN
- }
-};
diff --git a/frontend/src/components/Header.js b/frontend/src/components/Header.js
index b4a2177..119958b 100644
--- a/frontend/src/components/Header.js
+++ b/frontend/src/components/Header.js
@@ -18,21 +18,17 @@
import React, {Component} from 'react';
import Typed from 'typed.js';
import './Header.scss';
-import {filtersDefinitions, filtersNames} from "./filters/FiltersDefinitions";
import {Link, withRouter} from "react-router-dom";
import ButtonField from "./fields/ButtonField";
import ExitSearchFilter from "./filters/ExitSearchFilter";
+import {cleanNumber, validatePort} from "../utils";
+import StringConnectionsFilter from "./filters/StringConnectionsFilter";
+import RulesConnectionsFilter from "./filters/RulesConnectionsFilter";
+import BooleanConnectionsFilter from "./filters/BooleanConnectionsFilter";
+import AdvancedFilters from "./filters/AdvancedFilters";
class Header extends Component {
- constructor(props) {
- super(props);
- let newState = {};
- filtersNames.forEach(elem => newState[`${elem}_active`] = false);
- this.state = newState;
- this.fetchStateFromLocalStorage = this.fetchStateFromLocalStorage.bind(this);
- }
-
componentDidMount() {
const options = {
strings: ["caronte$ "],
@@ -40,33 +36,13 @@ class Header extends Component {
cursorChar: "❚"
};
this.typed = new Typed(this.el, options);
-
- this.fetchStateFromLocalStorage();
-
- if (typeof window !== "undefined") {
- window.addEventListener("quick-filters", this.fetchStateFromLocalStorage);
- }
}
componentWillUnmount() {
this.typed.destroy();
-
- if (typeof window) {
- window.removeEventListener("quick-filters", this.fetchStateFromLocalStorage);
- }
- }
-
- fetchStateFromLocalStorage() {
- let newState = {};
- filtersNames.forEach(elem => newState[`${elem}_active`] = localStorage.getItem(`filters.${elem}`) === "true");
- this.setState(newState);
}
render() {
- let quickFilters = filtersNames.filter(name => this.state[`${name}_active`])
- .map(name => <React.Fragment key={name}>{filtersDefinitions[name]}</React.Fragment>)
- .slice(0, 5);
-
return (
<header className="header container-fluid">
<div className="row">
@@ -82,14 +58,21 @@ class Header extends Component {
<div className="col-auto">
<div className="filters-bar">
- {quickFilters}
- <ExitSearchFilter />
+ <StringConnectionsFilter filterName="service_port"
+ defaultFilterValue="all_ports"
+ replaceFunc={cleanNumber}
+ validateFunc={validatePort}
+ key="service_port_filter"
+ width={200} small inline/>
+ <RulesConnectionsFilter/>
+ <BooleanConnectionsFilter filterName={"marked"}/>
+ <ExitSearchFilter/>
+ <AdvancedFilters onClick={this.props.onOpenFilters} />
</div>
</div>
<div className="col">
<div className="header-buttons">
- <ButtonField variant="pink" onClick={this.props.onOpenFilters} name="filters" bordered/>
<Link to={"/searches" + this.props.location.search}>
<ButtonField variant="pink" name="searches" bordered/>
</Link>
diff --git a/frontend/src/components/Header.scss b/frontend/src/components/Header.scss
index e2e8e1c..fff28e6 100644
--- a/frontend/src/components/Header.scss
+++ b/frontend/src/components/Header.scss
@@ -26,9 +26,16 @@
.filters-bar {
padding: 3px 0;
- .filter {
+ .filter,
+ .button-field {
display: inline-block;
margin-right: 10px;
}
+
+ .button-field button {
+ font-weight: 400;
+ padding: 7px 10px;
+ border-radius: 5px;
+ }
}
}
diff --git a/frontend/src/components/dialogs/Filters.js b/frontend/src/components/dialogs/Filters.js
index dfd554b..d2cce4f 100644
--- a/frontend/src/components/dialogs/Filters.js
+++ b/frontend/src/components/dialogs/Filters.js
@@ -16,91 +16,63 @@
*/
import React, {Component} from 'react';
-import {Col, Container, Modal, Row, Table} from "react-bootstrap";
-import {filtersDefinitions, filtersNames} from "../filters/FiltersDefinitions";
+import {Col, Container, Modal, Row} from "react-bootstrap";
import ButtonField from "../fields/ButtonField";
+import './Filters.scss';
+import {cleanNumber, validateIpAddress, validateMin, validatePort} from "../../utils";
+import StringConnectionsFilter from "../filters/StringConnectionsFilter";
class Filters extends Component {
- constructor(props) {
- super(props);
- let newState = {};
- filtersNames.forEach(elem => newState[`${elem}_active`] = false);
- this.state = newState;
- }
-
- componentDidMount() {
- let newState = {};
- filtersNames.forEach(elem => newState[`${elem}_active`] = localStorage.getItem(`filters.${elem}`) === "true");
- this.setState(newState);
- }
-
- checkboxChangesHandler(filterName, event) {
- this.setState({[`${filterName}_active`]: event.target.checked});
- localStorage.setItem(`filters.${filterName}`, event.target.checked);
- if (typeof window !== "undefined") {
- window.dispatchEvent(new Event("quick-filters"));
- }
- }
-
- generateRows(filtersNames) {
- return filtersNames.map(name =>
- <tr key={name}>
- <td><input type="checkbox"
- checked={this.state[`${name}_active`]}
- onChange={event => this.checkboxChangesHandler(name, event)}/></td>
- <td>{filtersDefinitions[name]}</td>
- </tr>
- );
- }
-
render() {
return (
<Modal
{...this.props}
- show="true"
+ show={true}
size="lg"
aria-labelledby="filters-dialog"
centered
>
<Modal.Header>
<Modal.Title id="filters-dialog">
- ~/filters
+ ~/advanced_filters
</Modal.Title>
</Modal.Header>
<Modal.Body>
- <Container>
- <Row>
- <Col md={6}>
- <Table borderless size="sm" className="filters-table">
- <thead>
- <tr>
- <th>show</th>
- <th>filter</th>
- </tr>
- </thead>
- <tbody>
- {this.generateRows(["service_port", "client_address", "min_duration",
- "min_bytes"])}
- </tbody>
- </Table>
- </Col>
- <Col md={6}>
- <Table borderless size="sm" className="filters-table">
- <thead>
- <tr>
- <th>show</th>
- <th>filter</th>
- </tr>
- </thead>
- <tbody>
- {this.generateRows(["matched_rules", "client_port", "max_duration",
- "max_bytes", "marked"])}
- </tbody>
- </Table>
- </Col>
- </Row>
- </Container>
+ <div className="advanced-filters d-flex">
+ <div className="flex-fill">
+ <StringConnectionsFilter filterName="client_address"
+ defaultFilterValue="all_addresses"
+ validateFunc={validateIpAddress}
+ key="client_address_filter"/>
+ <StringConnectionsFilter filterName="min_duration"
+ defaultFilterValue="0"
+ replaceFunc={cleanNumber}
+ validateFunc={validateMin(0)}
+ key="min_duration_filter"/>
+ <StringConnectionsFilter filterName="min_bytes"
+ defaultFilterValue="0"
+ replaceFunc={cleanNumber}
+ validateFunc={validateMin(0)}
+ key="min_bytes_filter"/>
+ </div>
+
+ <div className="flex-fill">
+ <StringConnectionsFilter filterName="client_port"
+ defaultFilterValue="all_ports"
+ replaceFunc={cleanNumber}
+ validateFunc={validatePort}
+ key="client_port_filter"/>
+ <StringConnectionsFilter filterName="max_duration"
+ defaultFilterValue="∞"
+ replaceFunc={cleanNumber}
+ key="max_duration_filter"/>
+ <StringConnectionsFilter filterName="max_bytes"
+ defaultFilterValue="∞"
+ replaceFunc={cleanNumber}
+ key="max_bytes_filter"/>
+ </div>
+ </div>
</Modal.Body>
<Modal.Footer className="dialog-footer">
<ButtonField variant="red" bordered onClick={this.props.onHide} name="close"/>
diff --git a/frontend/src/components/dialogs/Filters.scss b/frontend/src/components/dialogs/Filters.scss
new file mode 100644
index 0000000..7d09380
--- /dev/null
+++ b/frontend/src/components/dialogs/Filters.scss
@@ -0,0 +1,5 @@
+.advanced-filters {
+ .filter {
+ margin: 10px;
+ }
+}
diff --git a/frontend/src/components/fields/ButtonField.js b/frontend/src/components/fields/ButtonField.js
index 193339c..850f837 100644
--- a/frontend/src/components/fields/ButtonField.js
+++ b/frontend/src/components/fields/ButtonField.js
@@ -55,7 +55,8 @@ class ButtonField extends Component {
}
return (
- <div className={classNames("field", "button-field", {"field-small": this.props.small})}>
+ <div className={classNames("field", "button-field", {"field-small": this.props.small},
+ {"field-active": this.props.active})}>
<button type="button" className={classNames(buttonClassnames)}
onClick={handler} style={buttonStyle}>{this.props.name}</button>
</div>
diff --git a/frontend/src/components/fields/ButtonField.scss b/frontend/src/components/fields/ButtonField.scss
index 9e46b9f..99afe08 100644
--- a/frontend/src/components/fields/ButtonField.scss
+++ b/frontend/src/components/fields/ButtonField.scss
@@ -15,6 +15,13 @@
}
}
+ &.field-active {
+ button {
+ color: $color-primary-1;
+ background-color: $color-primary-4;
+ }
+ }
+
.button-variant-red {
color: $color-red-light;
background-color: $color-red;
diff --git a/frontend/src/components/filters/AdvancedFilters.js b/frontend/src/components/filters/AdvancedFilters.js
new file mode 100644
index 0000000..f5ba825
--- /dev/null
+++ b/frontend/src/components/filters/AdvancedFilters.js
@@ -0,0 +1,54 @@
+/*
+ * This file is part of caronte (https://github.com/eciavatta/caronte).
+ * Copyright (c) 2020 Emiliano Ciavatta.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import React, {Component} from 'react';
+import {withRouter} from "react-router-dom";
+import dispatcher from "../../dispatcher";
+import ButtonField from "../fields/ButtonField";
+import {updateParams} from "../../utils";
+
+class AdvancedFilters extends Component {
+
+ state = {};
+
+ componentDidMount() {
+ this.urlParams = new URLSearchParams(this.props.location.search);
+
+ this.connectionsFiltersCallback = payload => {
+ this.urlParams = updateParams(this.urlParams, payload);
+ const active = ["client_address", "client_port", "min_duration", "max_duration", "min_bytes", "max_bytes"]
+ .some(f => this.urlParams.has(f));
+ if (this.state.active !== active) {
+ this.setState({active: active});
+ }
+ };
+ dispatcher.register("connections_filters", this.connectionsFiltersCallback);
+ }
+
+ componentWillUnmount() {
+ dispatcher.unregister(this.connectionsFiltersCallback);
+ }
+
+ render() {
+ return (
+ <ButtonField onClick={this.props.onClick} name="advanced_filters" small active={this.state.active}/>
+ );
+ }
+
+}
+
+export default withRouter(AdvancedFilters);
diff --git a/frontend/src/components/filters/BooleanConnectionsFilter.js b/frontend/src/components/filters/BooleanConnectionsFilter.js
index c611a0d..9558323 100644
--- a/frontend/src/components/filters/BooleanConnectionsFilter.js
+++ b/frontend/src/components/filters/BooleanConnectionsFilter.js
@@ -59,7 +59,7 @@ class BooleanConnectionsFilter extends Component {
return (
<div className="filter" style={{"width": `${this.props.width}px`}}>
<CheckField checked={this.toBoolean(this.state.filterActive)} name={this.props.filterName}
- onChange={this.filterChanged}/>
+ onChange={this.filterChanged} small/>
</div>
);
}
diff --git a/frontend/src/components/filters/FiltersDefinitions.js b/frontend/src/components/filters/FiltersDefinitions.js
deleted file mode 100644
index 9fb3b18..0000000
--- a/frontend/src/components/filters/FiltersDefinitions.js
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * This file is part of caronte (https://github.com/eciavatta/caronte).
- * Copyright (c) 2020 Emiliano Ciavatta.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, version 3.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-import {cleanNumber, validateIpAddress, validateMin, validatePort} from "../../utils";
-import StringConnectionsFilter from "./StringConnectionsFilter";
-import React from "react";
-import RulesConnectionsFilter from "./RulesConnectionsFilter";
-import BooleanConnectionsFilter from "./BooleanConnectionsFilter";
-
-export const filtersNames = ["service_port", "matched_rules", "client_address", "client_port",
- "min_duration", "max_duration", "min_bytes", "max_bytes", "marked"];
-
-export const filtersDefinitions = {
- service_port: <StringConnectionsFilter filterName="service_port"
- defaultFilterValue="all_ports"
- replaceFunc={cleanNumber}
- validateFunc={validatePort}
- key="service_port_filter"
- width={200}/>,
- matched_rules: <RulesConnectionsFilter/>,
- client_address: <StringConnectionsFilter filterName="client_address"
- defaultFilterValue="all_addresses"
- validateFunc={validateIpAddress}
- key="client_address_filter"
- width={320}/>,
- client_port: <StringConnectionsFilter filterName="client_port"
- defaultFilterValue="all_ports"
- replaceFunc={cleanNumber}
- validateFunc={validatePort}
- key="client_port_filter"
- width={200}/>,
- min_duration: <StringConnectionsFilter filterName="min_duration"
- defaultFilterValue="0"
- replaceFunc={cleanNumber}
- validateFunc={validateMin(0)}
- key="min_duration_filter"
- width={200}/>,
- max_duration: <StringConnectionsFilter filterName="max_duration"
- defaultFilterValue="∞"
- replaceFunc={cleanNumber}
- key="max_duration_filter"
- width={200}/>,
- min_bytes: <StringConnectionsFilter filterName="min_bytes"
- defaultFilterValue="0"
- replaceFunc={cleanNumber}
- validateFunc={validateMin(0)}
- key="min_bytes_filter"
- width={200}/>,
- max_bytes: <StringConnectionsFilter filterName="max_bytes"
- defaultFilterValue="∞"
- replaceFunc={cleanNumber}
- key="max_bytes_filter"
- width={200}/>,
- contains_string: <StringConnectionsFilter filterName="contains_string"
- defaultFilterValue=""
- key="contains_string_filter"
- width={320}/>,
- marked: <BooleanConnectionsFilter filterName={"marked"}/>
-};
diff --git a/frontend/src/components/filters/StringConnectionsFilter.js b/frontend/src/components/filters/StringConnectionsFilter.js
index c833220..18b3784 100644
--- a/frontend/src/components/filters/StringConnectionsFilter.js
+++ b/frontend/src/components/filters/StringConnectionsFilter.js
@@ -127,7 +127,7 @@ class StringConnectionsFilter extends Component {
<div className="filter" style={{"width": `${this.props.width}px`}}>
<InputField active={active} invalid={this.state.invalidValue} name={this.props.filterName}
placeholder={this.props.defaultFilterValue} onChange={this.filterChanged}
- value={this.state.fieldValue} inline={true} small={true}/>
+ value={this.state.fieldValue} inline={this.props.inline} small={this.props.small}/>
</div>
);
}
diff --git a/frontend/src/components/panels/ConnectionsPane.js b/frontend/src/components/panels/ConnectionsPane.js
index 33dd7c1..89859e6 100644
--- a/frontend/src/components/panels/ConnectionsPane.js
+++ b/frontend/src/components/panels/ConnectionsPane.js
@@ -26,6 +26,7 @@ import log from "../../log";
import ButtonField from "../fields/ButtonField";
import dispatcher from "../../dispatcher";
import {Redirect} from "react-router";
+import {updateParams} from "../../utils";
const classNames = require('classnames');
@@ -67,29 +68,16 @@ class ConnectionsPane extends Component {
this.loadConnections(additionalParams, urlParams, true).then(() => log.debug("Connections loaded"));
this.connectionsFiltersCallback = payload => {
- const params = this.state.urlParams;
- const initialParams = params.toString();
-
- Object.entries(payload).forEach(([key, value]) => {
- if (value == null) {
- params.delete(key);
- } else if (Array.isArray(value)) {
- params.delete(key);
- value.forEach(v => params.append(key, v));
- } else {
- params.set(key, value);
- }
- });
-
- if (initialParams === params.toString()) {
+ const newParams = updateParams(this.state.urlParams, payload);
+ if (this.state.urlParams.toString() === newParams.toString()) {
return;
}
log.debug("Update following url params:", payload);
this.queryStringRedirect = true;
- this.setState({urlParams});
+ this.setState({urlParams: newParams});
- this.loadConnections({limit: this.queryLimit}, urlParams)
+ this.loadConnections({limit: this.queryLimit}, newParams)
.then(() => log.info("ConnectionsPane reloaded after query string update"));
};
dispatcher.register("connections_filters", this.connectionsFiltersCallback);
diff --git a/frontend/src/dispatcher.js b/frontend/src/dispatcher.js
index fa08d48..881970f 100644
--- a/frontend/src/dispatcher.js
+++ b/frontend/src/dispatcher.js
@@ -47,7 +47,7 @@ class Dispatcher {
};
unregister = (callback) => {
- this.listeners = _.without(callback);
+ _.remove(this.listeners, l => l.callback === callback);
};
}
diff --git a/frontend/src/index.js b/frontend/src/index.js
index ca1273a..4bc2730 100644
--- a/frontend/src/index.js
+++ b/frontend/src/index.js
@@ -26,9 +26,9 @@ import notifications from "./notifications";
notifications.createWebsocket();
ReactDOM.render(
- <React.StrictMode>
- <App />
- </React.StrictMode>,
+ // <React.StrictMode>
+ <App />,
+ // </React.StrictMode>,
document.getElementById('root')
);
diff --git a/frontend/src/utils.js b/frontend/src/utils.js
index f333f0d..06414ac 100644
--- a/frontend/src/utils.js
+++ b/frontend/src/utils.js
@@ -146,3 +146,19 @@ export function downloadBlob(blob, fileName) {
a.click();
window.URL.revokeObjectURL(url);
}
+
+export function updateParams(urlParams, payload) {
+ const params = new URLSearchParams(urlParams.toString());
+ Object.entries(payload).forEach(([key, value]) => {
+ if (value == null) {
+ params.delete(key);
+ } else if (Array.isArray(value)) {
+ params.delete(key);
+ value.forEach(v => params.append(key, v));
+ } else {
+ params.set(key, value);
+ }
+ });
+
+ return params;
+}