From bc2310f18e9b49c9444420052ee16c173f829b72 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Sat, 22 May 2021 03:36:42 +0200 Subject: [PATCH 01/12] =?UTF-8?q?=F0=9F=92=A5=20Refactor=20some=20things?= =?UTF-8?q?=20to=20improve=20code=20quality?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config-overrides.js | 1 + nest_frontend/App.test.js | 1 - nest_frontend/objects/Condition.js | 92 +++++++++++++++++++++ nest_frontend/objects/Condition.test.js | 56 +++++++++++++ nest_frontend/objects/Errors.js | 104 ++++++++++++++++++++++++ nest_frontend/objects/MapArea.js | 34 ++++++++ nest_frontend/objects/TimeRay.js | 24 ++++++ nest_frontend/utils/Condition.js | 40 --------- nest_frontend/utils/Errors.js | 69 ---------------- package-lock.json | 11 +++ package.json | 1 + 11 files changed, 323 insertions(+), 110 deletions(-) create mode 100644 nest_frontend/objects/Condition.js create mode 100644 nest_frontend/objects/Condition.test.js create mode 100644 nest_frontend/objects/Errors.js create mode 100644 nest_frontend/objects/MapArea.js create mode 100644 nest_frontend/objects/TimeRay.js delete mode 100644 nest_frontend/utils/Condition.js delete mode 100644 nest_frontend/utils/Errors.js diff --git a/config-overrides.js b/config-overrides.js index 71b04c8..2f97f0e 100644 --- a/config-overrides.js +++ b/config-overrides.js @@ -11,6 +11,7 @@ module.exports = { config.roots = config.roots.map(root => root.replace("src", "nest_frontend")) config.collectCoverageFrom = config.collectCoverageFrom.map(root => root.replace("src", "nest_frontend")) config.testMatch = config.testMatch.map(root => root.replace("src", "nest_frontend")) + console.debug(config) return config; } } \ No newline at end of file diff --git a/nest_frontend/App.test.js b/nest_frontend/App.test.js index d820924..4dab584 100644 --- a/nest_frontend/App.test.js +++ b/nest_frontend/App.test.js @@ -1,4 +1,3 @@ -// Link.react.test.js import React from "react" import "@testing-library/jest-dom/extend-expect" import { render, screen } from "@testing-library/react" diff --git a/nest_frontend/objects/Condition.js b/nest_frontend/objects/Condition.js new file mode 100644 index 0000000..2754676 --- /dev/null +++ b/nest_frontend/objects/Condition.js @@ -0,0 +1,92 @@ +import { IconDefinition, faQuestionCircle } from "@fortawesome/free-solid-svg-icons" + + +/** + * Condition class for an undefined/unknown condition. + * + * See [the Condition spec](https://gitlab.steffo.eu/nest/g2-progetto/-/wikis/sprint-2/Specifica-delle-Conditions). + */ +export class Condition { + content + type + id + + constructor(type, content, id = null) { + this.content = content + this.type = type + this.id = id + } + + /** + * Get the condition as an object readable by the backend. + * + * @returns {{id, type, content}} + */ + serialize() { + return { + type: this.type, + content: this.content, + id: this.id, + } + } + + /** + * Display parameters for the badge representing this condition. + * + * @returns {{color: string, icon: IconDefinition, title, content}} + */ + display() { + return { + color: "Grey", + icon: faQuestionCircle, + title: this.id, + content: this.content, + } + } +} + + +/** + * Require a tweet to contain a specific hashtag to be gathered. + */ +export class ConditionHashtag extends Condition { + constructor(hashtag, id = null) { + super(0, hashtag, id) + } +} + + +/** + * Require a tweet to be posted by a certain user to be gathered. + */ +export class ConditionUser extends Condition { + constructor(user, id = null) { + super(5, user, id) + } +} + + +/** + * Require a tweet to be posted before or after a certain time to be gathered. + */ +export class ConditionTime extends Condition { + timeRay + + constructor(timeRay, id = null) { + super(2, timeRay.toString(), id) + this.timeRay = timeRay + } +} + + +/** + * Require a tweet to have coordinates associated and to be posted within the {@link MapArea}. + */ +export class ConditionLocation extends Condition { + mapArea + + constructor(mapArea, id = null) { + super(3, mapArea.toString(), id) + this.mapArea = mapArea + } +} diff --git a/nest_frontend/objects/Condition.test.js b/nest_frontend/objects/Condition.test.js new file mode 100644 index 0000000..7fe651b --- /dev/null +++ b/nest_frontend/objects/Condition.test.js @@ -0,0 +1,56 @@ +import "@testing-library/jest-dom/extend-expect" +import { Condition, ConditionHashtag, ConditionLocation, ConditionTime, ConditionUser } from "./Condition" +import TimeRay from "./TimeRay" +import MapArea from "./MapArea" + + +test("Condition can be constructed", () => { + expect(new Condition(0, "hi")).toBeTruthy() + expect(new Condition(0, "hi", 1)).toBeTruthy() +}) + +test("ConditionHashtag can be constructed", () => { + expect(new ConditionHashtag("PdS2021")).toBeTruthy() + expect(new ConditionHashtag("PdS2021", 1)).toBeTruthy() +}) + +test("ConditionUser can be constructed", () => { + expect(new ConditionUser("USteffo")).toBeTruthy() + expect(new ConditionUser("USteffo", 1)).toBeTruthy() +}) + +test("ConditionTime can be constructed", () => { + const now = new Date() + const timeRay = new TimeRay(true, now) + + expect(new ConditionTime(timeRay)).toBeTruthy() + expect(new ConditionTime(timeRay, 1)).toBeTruthy() +}) + +test("ConditionLocation can be constructed", () => { + const mapArea = new MapArea(1000, 0.000, 0.000) + + expect(new ConditionLocation(mapArea)).toBeTruthy() + expect(new ConditionLocation(mapArea, 1)).toBeTruthy() +}) + +test("ConditionHashtag has the correct type", () => { + expect(new ConditionHashtag("PdS2021").type).toBe(0) +}) + +test("ConditionUser has the correct type", () => { + expect(new ConditionUser("USteffo").type).toBe(5) +}) + +test("ConditionTime has the correct type", () => { + const now = new Date() + const timeRay = new TimeRay(true, now) + + expect(new ConditionTime(timeRay).type).toBe(5) +}) + +test("ConditionLocation has the correct type", () => { + const mapArea = new MapArea(1000, 0.000, 0.000) + + expect(new ConditionLocation(mapArea).type).toBe(3) +}) diff --git a/nest_frontend/objects/Errors.js b/nest_frontend/objects/Errors.js new file mode 100644 index 0000000..ada665d --- /dev/null +++ b/nest_frontend/objects/Errors.js @@ -0,0 +1,104 @@ +/** + * Error thrown when a function is not implemented in the current class/instance. + */ +class NotImplementedError { + name + + constructor(name) { + this.name = name + } +} + + +/** + * An error in the N.E.S.T. frontend-backend communication. + */ +class BackendCommunicationError { + +} + + +/** + * Error thrown when trying to access a backend view which doesn't exist or isn't allowed in the used hook. + */ +class ViewNotAllowedError extends BackendCommunicationError { + view + + constructor(view) { + super() + + this.view = view + } +} + + +/** + * Error thrown when trying to access a backend view when outside a {@link ContextServer}. + */ +class ServerNotConfiguredError extends BackendCommunicationError { + +} + + +/** + * Error thrown when trying to access a backend view while another access is ongoing. + * + * This is not allowed due to potential race conditions. + */ +class FetchAlreadyRunningError extends BackendCommunicationError { + +} + + +/** + * Abstract class for {@link DecodeError} and {@link ResultError}. + */ +class FetchError extends BackendCommunicationError { + status + statusText + + constructor(status, statusText) { + super() + + this.status = status + this.statusText = statusText + } +} + + +/** + * Error thrown when the frontend can't parse the data received from the backend. + */ +class DecodeError extends FetchError { + error + + constructor(status, statusText, error) { + super(status, statusText) + + this.error = error + } +} + + +/** + * Error thrown when the backend returns a falsy `"result"` value. + */ +class ResultError extends FetchError { + status + statusText + data + + constructor(status, statusText, data) { + super(status, statusText) + + this.data = data + } + + getMsg() { + return this.data.msg + } + + getCode() { + return this.data.code + } +} diff --git a/nest_frontend/objects/MapArea.js b/nest_frontend/objects/MapArea.js new file mode 100644 index 0000000..4d23de4 --- /dev/null +++ b/nest_frontend/objects/MapArea.js @@ -0,0 +1,34 @@ +/** + * An area on a map, defined by a latitude `lat`, a longitude `lng` and a radius `rad` in meters. + */ +export default class MapArea { + /** + * @param rad - Radius of the area in meters. + * @param lat - Latitude of the center of the radius. + * @param lng - Longitude of the center of the radius. + */ + constructor(rad, lat, lng) { + this.rad = rad + this.lat = lat + this.lng = lng + } + + /** + * @returns {string} + */ + toString() { + return `${this.rad} ${this.lat.toFixed(7)} ${this.lng.toFixed(7)}` + } + + /** + * Render the MapArea as an human-readable string. + * + * @returns {string} + */ + toHumanString() { + if(this.rad >= 2000) { + const kmRadius = Math.round(this.rad / 1000) + return `${kmRadius}km ${this.lat.toFixed(3)} ${this.lng.toFixed(3)}` + } + } +} diff --git a/nest_frontend/objects/TimeRay.js b/nest_frontend/objects/TimeRay.js new file mode 100644 index 0000000..04bde14 --- /dev/null +++ b/nest_frontend/objects/TimeRay.js @@ -0,0 +1,24 @@ +/** + * An half-line of time, defined by a `date` and a boolean `isBefore` indicating if the time before or after the + * specified date should be selected. + */ +export default class TimeRay { + isBefore + date + + /** + * @param isBefore - `true` to select times earlier than the date, `false` to select times after the date. + * @param date - The date to start measurements from. + */ + constructor(isBefore, date) { + this.isBefore = isBefore + this.date = date + } + + /** + * @returns {string} + */ + toString() { + return `${this.isBefore ? "<" : ">"} ${this.date.toISOString()}` + } +} diff --git a/nest_frontend/utils/Condition.js b/nest_frontend/utils/Condition.js deleted file mode 100644 index 10c061f..0000000 --- a/nest_frontend/utils/Condition.js +++ /dev/null @@ -1,40 +0,0 @@ -import isString from "is-string" - - -const typeEnums = { - "HASHTAG": 0, - "TIME": 2, - "COORDINATES": 3, - "PLACE": 4, - "USER": 5, -} - - -/** - * A search/filtering Condition. - * - * See https://gitlab.steffo.eu/nest/g2-progetto/-/wikis/Specifica-delle-Conditions . - */ -export default class Condition { - - /** - * Create a new Condition. - * - * @param type - The type of Condition to create. - * It can be a number or one of the following strings: - * `"hashtag"`, `"time"`, `"coordinates"`, `"place"`. - * @param content - The content of the Condition. - * @param id - The id of the Condition on the backend, or null if the Condition hasn't been committed yet. - */ - constructor(type, content, id = null) { - if(isString(type)) { - this.type = typeEnums[type.toUpperCase()] - } - else { - this.type = type - } - - this.content = content - this.id = id - } -} diff --git a/nest_frontend/utils/Errors.js b/nest_frontend/utils/Errors.js deleted file mode 100644 index f4ba224..0000000 --- a/nest_frontend/utils/Errors.js +++ /dev/null @@ -1,69 +0,0 @@ -class NestError { - -} - - -class ViewNotAllowedError extends NestError { - view - - constructor(view) { - super() - - this.view = view - } -} - - -class ServerNotConfiguredError extends NestError { - -} - - -class FetchAlreadyRunningError extends NestError { - -} - - -class FetchError extends NestError { - status - statusText - - constructor(status, statusText) { - super() - - this.status = status - this.statusText = statusText - } -} - - -class DecodeError extends FetchError { - error - - constructor(status, statusText, error) { - super(status, statusText) - - this.error = error - } -} - - -class ResultError extends FetchError { - status - statusText - data - - constructor(status, statusText, data) { - super(status, statusText) - - this.data = data - } - - getMsg() { - return this.data.msg - } - - getCode() { - return this.data.code - } -} diff --git a/package-lock.json b/package-lock.json index bf22cb9..ac710f9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "@testing-library/user-event": "^12.8.3", "chart.js": "^3.2.1", "classnames": "^2.3.1", + "geolib": "^3.3.1", "is-string": "^1.0.5", "leaflet": "^1.7.1", "react": "^17.0.2", @@ -9416,6 +9417,11 @@ "node": ">=6.9.0" } }, + "node_modules/geolib": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/geolib/-/geolib-3.3.1.tgz", + "integrity": "sha512-sfahBXFcgELdpumDZV5b3KWiINkZxC5myAkLk067UUcTmTXaiE9SWmxMEHztn/Eus4JX6kesHxaIuZlniYgUtg==" + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -30086,6 +30092,11 @@ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" }, + "geolib": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/geolib/-/geolib-3.3.1.tgz", + "integrity": "sha512-sfahBXFcgELdpumDZV5b3KWiINkZxC5myAkLk067UUcTmTXaiE9SWmxMEHztn/Eus4JX6kesHxaIuZlniYgUtg==" + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", diff --git a/package.json b/package.json index f79bf75..2b3bfa4 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@testing-library/user-event": "^12.8.3", "chart.js": "^3.2.1", "classnames": "^2.3.1", + "geolib": "^3.3.1", "is-string": "^1.0.5", "leaflet": "^1.7.1", "react": "^17.0.2", From 7ca7ae49a598e8d3bbd506e5deb6180c83003e0c Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Sat, 22 May 2021 03:50:04 +0200 Subject: [PATCH 02/12] =?UTF-8?q?=E2=9C=A8=20Add=20Coordinates=20class=20a?= =?UTF-8?q?nd=20some=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nest_frontend/objects/Condition.test.js | 6 ++-- nest_frontend/objects/Coordinates.js | 42 +++++++++++++++++++++++++ nest_frontend/objects/MapArea.js | 39 ++++++++++++++++------- nest_frontend/objects/MapArea.test.js | 13 ++++++++ 4 files changed, 85 insertions(+), 15 deletions(-) create mode 100644 nest_frontend/objects/Coordinates.js create mode 100644 nest_frontend/objects/MapArea.test.js diff --git a/nest_frontend/objects/Condition.test.js b/nest_frontend/objects/Condition.test.js index 7fe651b..ee838af 100644 --- a/nest_frontend/objects/Condition.test.js +++ b/nest_frontend/objects/Condition.test.js @@ -1,7 +1,7 @@ -import "@testing-library/jest-dom/extend-expect" import { Condition, ConditionHashtag, ConditionLocation, ConditionTime, ConditionUser } from "./Condition" import TimeRay from "./TimeRay" import MapArea from "./MapArea" +import Coordinates from "./Coordinates" test("Condition can be constructed", () => { @@ -28,7 +28,7 @@ test("ConditionTime can be constructed", () => { }) test("ConditionLocation can be constructed", () => { - const mapArea = new MapArea(1000, 0.000, 0.000) + const mapArea = new MapArea(1000, new Coordinates(0.000, 0.000)) expect(new ConditionLocation(mapArea)).toBeTruthy() expect(new ConditionLocation(mapArea, 1)).toBeTruthy() @@ -50,7 +50,7 @@ test("ConditionTime has the correct type", () => { }) test("ConditionLocation has the correct type", () => { - const mapArea = new MapArea(1000, 0.000, 0.000) + const mapArea = new MapArea(1000, new Coordinates(0.000, 0.000)) expect(new ConditionLocation(mapArea).type).toBe(3) }) diff --git a/nest_frontend/objects/Coordinates.js b/nest_frontend/objects/Coordinates.js new file mode 100644 index 0000000..9f4868a --- /dev/null +++ b/nest_frontend/objects/Coordinates.js @@ -0,0 +1,42 @@ +/** + * A pair of coordinates, latitude `lat` and longitude `lng`. + */ +export default class Coordinates { + lat + lng + + /** + * @param lat - Latitude. + * @param lng - Longitude. + */ + constructor(lat, lng) { + this.lat = lat + this.lng = lng + } + + /** + * @returns {string} + */ + toString() { + return `${this.lat.toFixed(7)} ${this.lng.toFixed(7)}` + } + + /** + * Render the Coordinates as an human-readable string. + * + * @returns {string} + */ + toHumanString() { + return `${this.lat.toFixed(3)} ${this.lng.toFixed(3)}` + } + + /** + * Transform the object in a Geolib compatible-one. + */ + toGeolib() { + return { + latitude: this.lat, + longitude: this.lng, + } + } +} diff --git a/nest_frontend/objects/MapArea.js b/nest_frontend/objects/MapArea.js index 4d23de4..9bd5b9e 100644 --- a/nest_frontend/objects/MapArea.js +++ b/nest_frontend/objects/MapArea.js @@ -1,23 +1,27 @@ +import {getDistance} from "geolib" + + /** - * An area on a map, defined by a latitude `lat`, a longitude `lng` and a radius `rad` in meters. + * An area on a map, defined by a `center` and a `radius` in meters. */ export default class MapArea { + radius + center + /** - * @param rad - Radius of the area in meters. - * @param lat - Latitude of the center of the radius. - * @param lng - Longitude of the center of the radius. + * @param radius - The radius of the area in meters. + * @param center - The center of the area. */ - constructor(rad, lat, lng) { - this.rad = rad - this.lat = lat - this.lng = lng + constructor(radius, center) { + this.radius = radius + this.center = center } /** * @returns {string} */ toString() { - return `${this.rad} ${this.lat.toFixed(7)} ${this.lng.toFixed(7)}` + return `${this.radius} ${this.center.toString()}` } /** @@ -26,9 +30,20 @@ export default class MapArea { * @returns {string} */ toHumanString() { - if(this.rad >= 2000) { - const kmRadius = Math.round(this.rad / 1000) - return `${kmRadius}km ${this.lat.toFixed(3)} ${this.lng.toFixed(3)}` + if(this.radius >= 2000) { + const kmRadius = Math.round(this.radius / 1000) + return `${kmRadius}km ${this.center.toHumanString()}` } + return `${this.radius}m ${this.center.toHumanString()}` + } + + /** + * Check if a pair of coordinates is included in the area. + * + * @param coords - The coordinates to check. + * @returns {boolean} + */ + includes(coords) { + return getDistance(this.center.toGeolib(), coords.toGeolib()) <= this.radius } } diff --git a/nest_frontend/objects/MapArea.test.js b/nest_frontend/objects/MapArea.test.js new file mode 100644 index 0000000..409f015 --- /dev/null +++ b/nest_frontend/objects/MapArea.test.js @@ -0,0 +1,13 @@ +import Coordinates from "./Coordinates" +import MapArea from "./MapArea" + + +test("MapArea can be constructed", () => { + const mapArea = new MapArea(1000, new Coordinates(0.0, 0.0)) + expect(mapArea).toBeTruthy() +}) + +test("MapArea can be rendered to a spec-compatible string", () => { + const mapArea = new MapArea(1000, new Coordinates(0.0, 0.0)) + expect(mapArea.toString()).toBe("1000 0.0000000 0.0000000") +}) From 2737e8c1a22fbc29231d9ecbdccf4409e61a08a9 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Sat, 22 May 2021 03:52:25 +0200 Subject: [PATCH 03/12] =?UTF-8?q?=F0=9F=94=A7=20Add=20display=20method=20t?= =?UTF-8?q?o=20Condition=20objects?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nest_frontend/objects/Condition.js | 47 ++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/nest_frontend/objects/Condition.js b/nest_frontend/objects/Condition.js index 2754676..aa94979 100644 --- a/nest_frontend/objects/Condition.js +++ b/nest_frontend/objects/Condition.js @@ -1,4 +1,11 @@ -import { IconDefinition, faQuestionCircle } from "@fortawesome/free-solid-svg-icons" +import { + IconDefinition, + faQuestionCircle, + faHashtag, + faAt, + faClock, + faLocationArrow, +} from "@fortawesome/free-solid-svg-icons" /** @@ -40,7 +47,7 @@ export class Condition { color: "Grey", icon: faQuestionCircle, title: this.id, - content: this.content, + children: this.content, } } } @@ -53,6 +60,15 @@ export class ConditionHashtag extends Condition { constructor(hashtag, id = null) { super(0, hashtag, id) } + + display() { + return { + color: "Grey", + icon: faHashtag, + title: this.id, + children: this.content, + } + } } @@ -63,6 +79,15 @@ export class ConditionUser extends Condition { constructor(user, id = null) { super(5, user, id) } + + display() { + return { + color: "Green", + icon: faAt, + title: this.id, + children: this.content, + } + } } @@ -76,6 +101,15 @@ export class ConditionTime extends Condition { super(2, timeRay.toString(), id) this.timeRay = timeRay } + + display() { + return { + color: "Yellow", + icon: faClock, + title: this.id, + children: this.content, + } + } } @@ -89,4 +123,13 @@ export class ConditionLocation extends Condition { super(3, mapArea.toString(), id) this.mapArea = mapArea } + + display() { + return { + color: "Red", + icon: faLocationArrow, + title: this.id, + children: this.mapArea.toHumanString(), + } + } } From 9c0356c8e4f2f2dceb59c5e87cc9a7e01c00405e Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Sat, 22 May 2021 04:08:39 +0200 Subject: [PATCH 04/12] =?UTF-8?q?=F0=9F=92=A5=20Refactor=20more=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/inspectionProfiles/Project_Default.xml | 2 +- nest_frontend/index.js | 1 + nest_frontend/objects/Coordinates.js | 25 ++++++++++++- nest_frontend/objects/MapArea.js | 17 ++++++++- .../Date.js} | 18 ++------- nest_frontend/prototypes/index.js | 1 + nest_frontend/utils/location.js | 37 ------------------- 7 files changed, 47 insertions(+), 54 deletions(-) rename nest_frontend/{utils/convertToLocalISODate.js => prototypes/Date.js} (59%) create mode 100644 nest_frontend/prototypes/index.js delete mode 100644 nest_frontend/utils/location.js diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 90c84e8..e5d68bf 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -58,7 +58,7 @@ - + diff --git a/nest_frontend/index.js b/nest_frontend/index.js index c9b7cd1..b931256 100644 --- a/nest_frontend/index.js +++ b/nest_frontend/index.js @@ -3,6 +3,7 @@ import ReactDOM from "react-dom" import "./index.css" import App from "./App" import reportWebVitals from "./reportWebVitals" +import "./prototypes" ReactDOM.render( diff --git a/nest_frontend/objects/Coordinates.js b/nest_frontend/objects/Coordinates.js index 9f4868a..10413f2 100644 --- a/nest_frontend/objects/Coordinates.js +++ b/nest_frontend/objects/Coordinates.js @@ -14,6 +14,20 @@ export default class Coordinates { this.lng = lng } + /** + * Create a new {@link Coordinates} from the format used by the backend. + * + * @param str - The string to create the object from. + * @returns {Coordinates} + */ + static fromCrawlerString(str) { + const match = /[{]([0-9.]+),([0-9.]+)[}]/.exec(str) + if(!match) { + throw new Error(`Invalid location string: ${str}`) + } + return new Coordinates(match[0], match[1]) + } + /** * @returns {string} */ @@ -31,7 +45,7 @@ export default class Coordinates { } /** - * Transform the object in a Geolib compatible-one. + * Transform this object in a Geolib compatible-one. */ toGeolib() { return { @@ -39,4 +53,13 @@ export default class Coordinates { longitude: this.lng, } } + + /** + * Transform this object in a 2-ple. + * + * @returns {[Number, Number]} + */ + toArray() { + return [this.lat, this.lng] + } } diff --git a/nest_frontend/objects/MapArea.js b/nest_frontend/objects/MapArea.js index 9bd5b9e..a222742 100644 --- a/nest_frontend/objects/MapArea.js +++ b/nest_frontend/objects/MapArea.js @@ -1,4 +1,5 @@ import {getDistance} from "geolib" +import osmZoomLevels from "../utils/osmZoomLevels" /** @@ -17,6 +18,20 @@ export default class MapArea { this.center = center } + /** + * Create a new {@link MapArea} from the [zoom level of OpenStreetMaps][1], assuming the window is + * ~400 pixels large. + * + * [1]: https://wiki.openstreetmap.org/wiki/Zoom_levels + * + * @param zoom + * @param center + * @returns {MapArea} + */ + static fromZoomLevel(zoom, center) { + return new MapArea(osmZoomLevels[zoom], center) + } + /** * @returns {string} */ @@ -25,7 +40,7 @@ export default class MapArea { } /** - * Render the MapArea as an human-readable string. + * Render the {@link MapArea} as an human-readable string. * * @returns {string} */ diff --git a/nest_frontend/utils/convertToLocalISODate.js b/nest_frontend/prototypes/Date.js similarity index 59% rename from nest_frontend/utils/convertToLocalISODate.js rename to nest_frontend/prototypes/Date.js index 8282621..79e80cf 100644 --- a/nest_frontend/utils/convertToLocalISODate.js +++ b/nest_frontend/prototypes/Date.js @@ -1,20 +1,10 @@ -// Wow, JS, davvero? -// Davvero tutte le date.toISOString() sono considerate UTC? -// Wow. - -/** - * Convert a {@link Date} object to a timezone aware ISO String, using the user's local timezone. - * - * @param date - * @returns {string} - */ -export default function convertToLocalISODate(date) { - if(date.toString() === "Invalid Date") { +Date.prototype.toAwareISOString = function() { + if(this.toString() === "Invalid Date") { throw new Error("Data non valida ricevuta come parametro.") } // Create a timezone naive ISO string - const naive = date.toISOString() + const naive = this.toISOString() // Find the local timezone const tz = -new Date().getTimezoneOffset() @@ -29,4 +19,4 @@ export default function convertToLocalISODate(date) { // Replace the naive part with the aware part return naive.replace("Z", `${tz < 0 ? "-" : "+"}${tzHours}${tzMinutes}`) -} \ No newline at end of file +} diff --git a/nest_frontend/prototypes/index.js b/nest_frontend/prototypes/index.js new file mode 100644 index 0000000..aff132e --- /dev/null +++ b/nest_frontend/prototypes/index.js @@ -0,0 +1 @@ +import "./Date" \ No newline at end of file diff --git a/nest_frontend/utils/location.js b/nest_frontend/utils/location.js deleted file mode 100644 index 561f3d0..0000000 --- a/nest_frontend/utils/location.js +++ /dev/null @@ -1,37 +0,0 @@ -export const locationRegex = /[{](?[0-9.]+),(?[0-9.]+)[}]/ - - -export class Location { - lat - lng - - constructor(lat, lng) { - this.lat = lat - this.lng = lng - } - - static fromString(locString) { - const match = locationRegex.exec(locString) - if(!match) { - throw new Error(`Invalid location string: ${locString}`) - } - const { lat, lng } = match.groups - return new Location(lat, lng) - } - - static fromTweet(tweet) { - if(tweet.location === null) { - throw new Error(`Tweet has no location: ${tweet}`) - } - - return Location.fromString(tweet.location) - } - - toArray() { - return [this.lat, this.lng] - } - - toString() { - return `${this.lat.toFixed(3)} ${this.lng.toFixed(3)}` - } -} From c9cf2a11426b5c369dc1dcea747dea8b567a33c1 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Sat, 22 May 2021 04:17:02 +0200 Subject: [PATCH 05/12] =?UTF-8?q?=F0=9F=92=A5=20Refactor=20some=20more...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nest_frontend/components/base/Button.js | 4 +-- .../components/base/ButtonSidebar.js | 4 +-- .../components/base/InputWithIcon.js | 4 +-- .../URLSearchParams.js} | 2 +- nest_frontend/prototypes/index.js | 3 +- nest_frontend/utils/defaultMapLocation.js | 2 -- nest_frontend/utils/goToOnSuccess.js | 1 - nest_frontend/utils/makeIcon.js | 29 +++++++++++++++++++ nest_frontend/utils/make_icon.js | 17 ----------- 9 files changed, 38 insertions(+), 28 deletions(-) rename nest_frontend/{utils/makeURLSearchParams.js => prototypes/URLSearchParams.js} (86%) delete mode 100644 nest_frontend/utils/defaultMapLocation.js create mode 100644 nest_frontend/utils/makeIcon.js delete mode 100644 nest_frontend/utils/make_icon.js diff --git a/nest_frontend/components/base/Button.js b/nest_frontend/components/base/Button.js index 1692c46..0a94884 100644 --- a/nest_frontend/components/base/Button.js +++ b/nest_frontend/components/base/Button.js @@ -1,7 +1,7 @@ import React from "react" import Style from "./Button.module.css" import classNames from "classnames" -import make_icon from "../../utils/make_icon" +import makeIcon from "../../utils/makeIcon" /** @@ -26,7 +26,7 @@ export default function Button({ children, disabled, onClick, className, color, disabled={disabled} {...props} > - {children} {make_icon(icon, Style.Icon)} + {children} {makeIcon(icon, {className: Style.Icon})} ) } diff --git a/nest_frontend/components/base/ButtonSidebar.js b/nest_frontend/components/base/ButtonSidebar.js index c9b4840..7e3911b 100644 --- a/nest_frontend/components/base/ButtonSidebar.js +++ b/nest_frontend/components/base/ButtonSidebar.js @@ -1,7 +1,7 @@ import React from "react" import Style from "./ButtonSidebar.module.css" import classNames from "classnames" -import make_icon from "../../utils/make_icon" +import makeIcon from "../../utils/makeIcon" import { Link } from "react-router-dom" import { useRouteMatch } from "react-router" @@ -31,7 +31,7 @@ export default function ButtonSidebar({ icon, children, to, className, ...props return (
- {make_icon(icon, Style.ButtonIcon)} + {makeIcon(icon, {className: Style.ButtonIcon})}
{children}
diff --git a/nest_frontend/components/base/InputWithIcon.js b/nest_frontend/components/base/InputWithIcon.js index bc84e3b..87934f4 100644 --- a/nest_frontend/components/base/InputWithIcon.js +++ b/nest_frontend/components/base/InputWithIcon.js @@ -1,7 +1,7 @@ import React, { useState } from "react" import Style from "./InputWithIcon.module.css" import classNames from "classnames" -import make_icon from "../../utils/make_icon" +import makeIcon from "../../utils/makeIcon" /** @@ -22,7 +22,7 @@ export default function InputWithIcon({ icon, className, ...props }) { return (
- {make_icon(icon)} + {makeIcon(icon)}
{ diff --git a/nest_frontend/utils/makeIcon.js b/nest_frontend/utils/makeIcon.js new file mode 100644 index 0000000..4a364ed --- /dev/null +++ b/nest_frontend/utils/makeIcon.js @@ -0,0 +1,29 @@ +import React, { isValidElement } from "react" +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" +import {IconDefinition} from "@fortawesome/fontawesome-svg-core" + + +/** + * Try to create an icon element based on what is passed to the function: + * - If a {@link JSX.Element} is passed, a `` element containing it will be created and returned. + * - If a {@link IconDefinition} is passed, a `` element containing a {@link FontAwesomeIcon} will be created + * and returned. + * - If a falsy value is passed, `null` will be returned. + * + * @param icon - The icon value. + * @param props - Props to pass to the span element when it is created. + * @returns {JSX.Element|null} + */ +export default function makeIcon(icon, props) { + if(isValidElement(icon)) { + return {icon} + } + else if(icon) { + return ( + + ) + } + else { + return null + } +} diff --git a/nest_frontend/utils/make_icon.js b/nest_frontend/utils/make_icon.js deleted file mode 100644 index 22f960d..0000000 --- a/nest_frontend/utils/make_icon.js +++ /dev/null @@ -1,17 +0,0 @@ -import React, { isValidElement } from "react" -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" - - -export default function make_icon(icon, className) { - if(isValidElement(icon)) { - return icon - } - else if(icon) { - return ( - - ) - } - else { - return null - } -} \ No newline at end of file From 919cbefd0f80e74e04a33221505f48db991484b6 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Sat, 22 May 2021 04:32:47 +0200 Subject: [PATCH 06/12] =?UTF-8?q?=F0=9F=92=A5=20Refactor=20even=20more...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../interactive/BoxFilterContains.js | 4 +- .../interactive/BoxFilterDatetime.js | 4 +- .../interactive/BoxFilterHasPlace.js | 4 +- .../interactive/BoxFilterHashtag.js | 4 +- .../interactive/BoxFilterLocation.js | 4 +- .../components/interactive/BoxFilterUser.js | 4 +- .../interactive/BoxVisualizationWordcloud.js | 4 +- nest_frontend/objects/Filter.js | 222 ++++++++++++++++++ nest_frontend/objects/MapArea.js | 6 +- nest_frontend/objects/MapArea.test.js | 2 +- nest_frontend/objects/TimeRay.js | 4 + nest_frontend/utils/Filter.js | 221 ----------------- 12 files changed, 244 insertions(+), 239 deletions(-) create mode 100644 nest_frontend/objects/Filter.js delete mode 100644 nest_frontend/utils/Filter.js diff --git a/nest_frontend/components/interactive/BoxFilterContains.js b/nest_frontend/components/interactive/BoxFilterContains.js index 4b57660..545d263 100644 --- a/nest_frontend/components/interactive/BoxFilterContains.js +++ b/nest_frontend/components/interactive/BoxFilterContains.js @@ -2,7 +2,7 @@ import React from "react" import BoxFull from "../base/BoxFull" import useRepositoryViewer from "../../hooks/useRepositoryViewer" import useStrings from "../../hooks/useStrings" -import { ContainsFilter } from "../../utils/Filter" +import { FilterContains } from "../../utils/Filter" import FormInlineText from "./FormInlineText" import { faFont } from "@fortawesome/free-solid-svg-icons" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" @@ -13,7 +13,7 @@ export default function BoxFilterContains({ ...props }) { const { appendFilter } = useRepositoryViewer() const submit = value => { - appendFilter(new ContainsFilter(false, value)) + appendFilter(new FilterContains(false, value)) } // TODO: add this string diff --git a/nest_frontend/components/interactive/BoxFilterDatetime.js b/nest_frontend/components/interactive/BoxFilterDatetime.js index 8fde916..ab0c1ff 100644 --- a/nest_frontend/components/interactive/BoxFilterDatetime.js +++ b/nest_frontend/components/interactive/BoxFilterDatetime.js @@ -3,7 +3,7 @@ import BoxFull from "../base/BoxFull" import { faClock, faHashtag } from "@fortawesome/free-solid-svg-icons" import useRepositoryViewer from "../../hooks/useRepositoryViewer" import useStrings from "../../hooks/useStrings" -import { AfterDatetimeFilter } from "../../utils/Filter" +import { FilterInsideTimeRay } from "../../utils/Filter" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" import FormInlineBADatetime from "./FormInlineBADatetime" @@ -13,7 +13,7 @@ export default function BoxFilterDatetime({ ...props }) { const { appendFilter } = useRepositoryViewer() const submit = ({ date, isBefore }) => { - appendFilter(new AfterDatetimeFilter(isBefore, date)) + appendFilter(new FilterInsideTimeRay(isBefore, date)) } return ( diff --git a/nest_frontend/components/interactive/BoxFilterHasPlace.js b/nest_frontend/components/interactive/BoxFilterHasPlace.js index b317b18..ffaa711 100644 --- a/nest_frontend/components/interactive/BoxFilterHasPlace.js +++ b/nest_frontend/components/interactive/BoxFilterHasPlace.js @@ -5,7 +5,7 @@ import useRepositoryViewer from "../../hooks/useRepositoryViewer" import useStrings from "../../hooks/useStrings" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" import { faLocationArrow, faPlus } from "@fortawesome/free-solid-svg-icons" -import { HasPlaceFilter } from "../../utils/Filter" +import { FilterWithPlace } from "../../utils/Filter" import ButtonIconOnly from "../base/ButtonIconOnly" @@ -15,7 +15,7 @@ export default function BoxFilterHasPlace({ ...props }) { const { appendFilter } = useRepositoryViewer() const submit = () => { - appendFilter(new HasPlaceFilter(false)) + appendFilter(new FilterWithPlace(false)) } // TODO: translate this diff --git a/nest_frontend/components/interactive/BoxFilterHashtag.js b/nest_frontend/components/interactive/BoxFilterHashtag.js index 76aba03..211d461 100644 --- a/nest_frontend/components/interactive/BoxFilterHashtag.js +++ b/nest_frontend/components/interactive/BoxFilterHashtag.js @@ -3,7 +3,7 @@ import BoxFull from "../base/BoxFull" import { faClock } from "@fortawesome/free-solid-svg-icons" import useRepositoryViewer from "../../hooks/useRepositoryViewer" import useStrings from "../../hooks/useStrings" -import { HashtagFilter } from "../../utils/Filter" +import { FilterHashtag } from "../../utils/Filter" import FormInlineHashtag from "./FormInlineHashtag" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" @@ -13,7 +13,7 @@ export default function BoxFilterHashtag({ ...props }) { const { appendFilter } = useRepositoryViewer() const submit = value => { - appendFilter(new HashtagFilter(false, value)) + appendFilter(new FilterHashtag(false, value)) } return ( diff --git a/nest_frontend/components/interactive/BoxFilterLocation.js b/nest_frontend/components/interactive/BoxFilterLocation.js index 851dc49..5c036f7 100644 --- a/nest_frontend/components/interactive/BoxFilterLocation.js +++ b/nest_frontend/components/interactive/BoxFilterLocation.js @@ -6,7 +6,7 @@ import useStrings from "../../hooks/useStrings" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" import { faMapPin } from "@fortawesome/free-solid-svg-icons" import FormInlineLocation from "./FormInlineLocation" -import { LocationRadiusFilter } from "../../utils/Filter" +import { FilterInsideMapArea } from "../../utils/Filter" export default function BoxFilterLocation({ ...props }) { @@ -15,7 +15,7 @@ export default function BoxFilterLocation({ ...props }) { const { appendFilter, mapViewHook } = useRepositoryViewer() const submit = () => { - appendFilter(new LocationRadiusFilter(false, mapViewHook.center, mapViewHook.radius)) + appendFilter(new FilterInsideMapArea(false, mapViewHook.center, mapViewHook.radius)) } return ( diff --git a/nest_frontend/components/interactive/BoxFilterUser.js b/nest_frontend/components/interactive/BoxFilterUser.js index 9efd651..edcb702 100644 --- a/nest_frontend/components/interactive/BoxFilterUser.js +++ b/nest_frontend/components/interactive/BoxFilterUser.js @@ -3,7 +3,7 @@ import BoxFull from "../base/BoxFull" import { faAt } from "@fortawesome/free-solid-svg-icons" import useRepositoryViewer from "../../hooks/useRepositoryViewer" import useStrings from "../../hooks/useStrings" -import { UserFilter } from "../../utils/Filter" +import { FilterPoster } from "../../utils/Filter" import FormInlineUser from "./FormInlineUser" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" @@ -16,7 +16,7 @@ export default function BoxFilterUser({ ...props }) { const { appendFilter } = useRepositoryViewer() const submit = value => { - appendFilter(new UserFilter(false, value)) + appendFilter(new FilterPoster(false, value)) } return ( diff --git a/nest_frontend/components/interactive/BoxVisualizationWordcloud.js b/nest_frontend/components/interactive/BoxVisualizationWordcloud.js index 9072146..be53923 100644 --- a/nest_frontend/components/interactive/BoxVisualizationWordcloud.js +++ b/nest_frontend/components/interactive/BoxVisualizationWordcloud.js @@ -4,7 +4,7 @@ import ContextLanguage from "../../contexts/ContextLanguage" import BoxFull from "../base/BoxFull" import Empty from "./Empty" import ContextRepositoryViewer from "../../contexts/ContextRepositoryViewer" -import { ContainsFilter } from "../../utils/Filter" +import { FilterContains } from "../../utils/Filter" export default function BoxVisualizationWordcloud({ ...props }) { @@ -20,7 +20,7 @@ export default function BoxVisualizationWordcloud({ ...props }) { } const onWordClick = word => { - appendFilter(new ContainsFilter(false, word.text)) + appendFilter(new FilterContains(false, word.text)) } return ( diff --git a/nest_frontend/objects/Filter.js b/nest_frontend/objects/Filter.js new file mode 100644 index 0000000..c8f8892 --- /dev/null +++ b/nest_frontend/objects/Filter.js @@ -0,0 +1,222 @@ +import { + faAt, + faClock, + faFilter, + faFont, + faHashtag, + faLocationArrow, + faMapMarkerAlt, + faMapPin, +} from "@fortawesome/free-solid-svg-icons" + + +/** + * A filter applicable in the Analysis mode. + */ +export class Filter { + negate + + /** + * @param negate - If the filter output should be reversed. + */ + constructor(negate = false) { + this.negate = negate + } + + /** + * Check if a tweet passed through the filter or not, without applying `negate`. + * + * @param tweet - The tweet to check. + * @returns {boolean} + */ + check(tweet) { + return true + } + + /** + * Check if a tweet passed through the filter or not, applying `negate`. + * + * @param tweet - The tweet to check. + * @returns {boolean} + */ + exec(tweet) { + return Boolean(this.check(tweet) ^ this.negate) + } + + display() { + return { + color: "Grey", + icon: faFilter, + children: this.negate ? "False" : "True" + } + } +} + + +/** + * Checks if a tweet contains a string. + */ +export class FilterContains extends Filter { + string + + constructor(word, negate = false) { + super(negate) + this.string = word.toLowerCase().trim() + } + + check(tweet) { + return tweet.content?.toLowerCase().includes(this.string) + } + + display() { + return { + color: "Grey", + icon: faFont, + children: this.string + } + } +} + + +/** + * Check if a tweet contains an hashtag. + */ +export class FilterHashtag extends FilterContains { + hashtag + + constructor(hashtag, negate = false) { + super(negate, `#${hashtag}`) + this.hashtag = hashtag + } + + display() { + return { + color: "Grey", + icon: faHashtag, + children: this.hashtag + } + } +} + + +/** + * Check if a tweet was posted by a certain user. + */ +export class FilterPoster extends Filter { + poster + + constructor(poster, negate = false) { + super(negate) + this.poster = poster + } + + check(tweet) { + return tweet.poster.toLowerCase() === this.poster.toLowerCase() + } + + display() { + return { + color: "Green", + icon: faAt, + children: this.poster + } + } +} + + +/** + * Check if a tweet contains `location` metadata. + */ +export class FilterWithLocation extends Filter { + constructor(negate = false) { + super(negate) + } + + check(tweet) { + return Boolean(tweet["location"]) + } + + display() { + return { + color: "Red", + icon: faLocationArrow, + children: "" + } + } +} + + +/** + * Check if a tweet contains `place` metadata. + */ +export class FilterWithPlace extends Filter { + constructor(negate = false) { + super(negate) + } + + check(tweet) { + return Boolean(tweet["place"]) + } + + display() { + return { + color: "Red", + icon: faMapMarkerAlt, + children: "" + } + } +} + + +/** + * Check if a tweet's `location` is inside a {@link MapArea}. + */ +export class FilterInsideMapArea extends FilterWithLocation { + mapArea + + constructor(mapArea, negate = false) { + super(negate) + this.mapArea = mapArea + } + + check(tweet) { + if(!super.check(tweet)) { + return false + } + + return this.mapArea.includes(tweet.location) + } + + display() { + return { + color: "Red", + icon: faLocationArrow, + children: this.mapArea.toHumanString() + } + } +} + + +/** + * Check if a tweet's `post_time` is inside a {@link TimeRay}. + */ +export class FilterInsideTimeRay extends Filter { + timeRay + + constructor(timeRay, negate = false) { + super(negate) + this.timeRay = timeRay + } + + check(tweet) { + return this.datetime < new Date(tweet["insert_time"]) + } + + display() { + return { + color: "Yellow", + icon: faClock, + children: this.timeRay.toString() + } + } +} diff --git a/nest_frontend/objects/MapArea.js b/nest_frontend/objects/MapArea.js index a222742..f1f21ac 100644 --- a/nest_frontend/objects/MapArea.js +++ b/nest_frontend/objects/MapArea.js @@ -36,7 +36,7 @@ export default class MapArea { * @returns {string} */ toString() { - return `${this.radius} ${this.center.toString()}` + return `< ${this.radius} ${this.center.toString()}` } /** @@ -47,9 +47,9 @@ export default class MapArea { toHumanString() { if(this.radius >= 2000) { const kmRadius = Math.round(this.radius / 1000) - return `${kmRadius}km ${this.center.toHumanString()}` + return `< ${kmRadius}km ${this.center.toHumanString()}` } - return `${this.radius}m ${this.center.toHumanString()}` + return `< ${this.radius}m ${this.center.toHumanString()}` } /** diff --git a/nest_frontend/objects/MapArea.test.js b/nest_frontend/objects/MapArea.test.js index 409f015..cf64005 100644 --- a/nest_frontend/objects/MapArea.test.js +++ b/nest_frontend/objects/MapArea.test.js @@ -9,5 +9,5 @@ test("MapArea can be constructed", () => { test("MapArea can be rendered to a spec-compatible string", () => { const mapArea = new MapArea(1000, new Coordinates(0.0, 0.0)) - expect(mapArea.toString()).toBe("1000 0.0000000 0.0000000") + expect(mapArea.toString()).toBe("< 1000 0.0000000 0.0000000") }) diff --git a/nest_frontend/objects/TimeRay.js b/nest_frontend/objects/TimeRay.js index 04bde14..fcd2184 100644 --- a/nest_frontend/objects/TimeRay.js +++ b/nest_frontend/objects/TimeRay.js @@ -21,4 +21,8 @@ export default class TimeRay { toString() { return `${this.isBefore ? "<" : ">"} ${this.date.toISOString()}` } + + includes(date) { + return Boolean((this.date > date) ^ this.isBefore) + } } diff --git a/nest_frontend/utils/Filter.js b/nest_frontend/utils/Filter.js deleted file mode 100644 index 0f6b737..0000000 --- a/nest_frontend/utils/Filter.js +++ /dev/null @@ -1,221 +0,0 @@ -import { Location } from "./location" -import { - faAt, - faClock, - faFilter, - faFont, - faHashtag, - faLocationArrow, - faMapMarkerAlt, - faMapPin, -} from "@fortawesome/free-solid-svg-icons" - - -export class Filter { - negate - - constructor(negate) { - this.negate = negate - } - - check(tweet) { - return true - } - - exec(tweet) { - return this.check(tweet) ^ this.negate - } - - color() { - return "Grey" - } - - icon() { - return faFilter - } - - text() { - return this.negate ? "False" : "True" - } -} - - -export class ContainsFilter extends Filter { - word - - constructor(negate, word) { - super(negate) - this.word = word.toLowerCase().trim() - } - - check(tweet) { - return tweet.content?.toLowerCase().includes(this.word) - } - - color() { - return "Grey" - } - - icon() { - return faFont - } - - text() { - return this.word - } -} - - -export class HashtagFilter extends ContainsFilter { - hashtag - - constructor(negate, hashtag) { - super(negate, `#${hashtag}`) - this.hashtag = hashtag - } - - icon() { - return faHashtag - } - - text() { - return this.hashtag - } -} - - -export class UserFilter extends Filter { - user - - constructor(negate, user) { - super(negate) - this.user = user.toLowerCase().trim().replace(/^@/, "") - } - - check(tweet) { - return tweet.poster.toLowerCase() === this.user - } - - color() { - return "Green" - } - - icon() { - return faAt - } - - text() { - return this.user - } -} - - -export class HasLocationFilter extends Filter { - constructor(negate) { - super(negate) - } - - check(tweet) { - return Boolean(tweet["location"]) - } - - color() { - return "Red" - } - - icon() { - return faMapMarkerAlt - } - - text() { - return "" - } -} - - -export class HasPlaceFilter extends Filter { - constructor(negate) { - super(negate) - } - - check(tweet) { - return Boolean(tweet["place"]) - } - - color() { - return "Red" - } - - icon() { - return faLocationArrow - } - - text() { - return "" - } -} - - -export class LocationRadiusFilter extends HasLocationFilter { - center - radius - - constructor(negate, center, radius) { - super(negate) - this.center = center - this.radius = radius - } - - check(tweet) { - if(!super.check(tweet)) { - return false - } - - // FIXME: Maths is hard - const location = Location.fromTweet(tweet) - const latDiff = Math.abs(location.lat - this.center.lat) - const lngDiff = Math.abs(location.lng - this.center.lng) - const squaredDistance = Math.pow(latDiff, 2) + Math.pow(lngDiff, 2) - const squaredRadius = Math.pow(this.radius, 2) - - return squaredDistance < squaredRadius - } - - color() { - return "Red" - } - - icon() { - return faMapPin - } - - text() { - return `< ${this.radius}m ${this.center.lat.toFixed(3)} ${this.center.lng.toFixed(3)}` - } -} - - -export class AfterDatetimeFilter extends Filter { - datetime - - constructor(negate, datetime) { - super(negate) - this.datetime = datetime - } - - check(tweet) { - return this.datetime < new Date(tweet["insert_time"]) - } - - color() { - return "Yellow" - } - - icon() { - return faClock - } - - text() { - return `${this.negate ? "<" : ">"} ${this.datetime.toISOString()}` - } -} \ No newline at end of file From 36f7f763729398636a7fa8fb3df32f96bdfab68a Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Sat, 22 May 2021 04:35:37 +0200 Subject: [PATCH 07/12] =?UTF-8?q?=F0=9F=8C=90=20Fix=20debug=20messages=20l?= =?UTF-8?q?anguage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nest_frontend/hooks/useArrayState.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nest_frontend/hooks/useArrayState.js b/nest_frontend/hooks/useArrayState.js index 9a8225c..5aeda3d 100644 --- a/nest_frontend/hooks/useArrayState.js +++ b/nest_frontend/hooks/useArrayState.js @@ -2,7 +2,7 @@ import { useCallback, useState } from "react" /** - * An hook similar to {@link useState} which stores an array of values. + * An hook similar to {@link useState} which stores an array instead of a single value. * * @param def - The starting value of the hook. * @returns {{spliceValue, removeValue, setValue, appendValue, value}} @@ -12,7 +12,7 @@ export default function useArrayState(def) { const appendValue = useCallback( newSingle => { - console.debug("Aggiungendo ", newSingle, " ad ArrayState") + console.debug("Adding ", newSingle, " to ArrayState") setValue( oldArray => [...oldArray, newSingle], ) @@ -22,7 +22,7 @@ export default function useArrayState(def) { const spliceValue = useCallback( position => { - console.debug("Estraendo ", position, " da ArrayState") + console.debug("Splicing ", position, " from ArrayState") setValue( oldArray => { oldArray.splice(position, 1) From f27304e34c13ac1cda4d48568ceea72aa3adc3bb Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Sat, 22 May 2021 04:36:08 +0200 Subject: [PATCH 08/12] =?UTF-8?q?=F0=9F=97=91=20Remove=20unused=20hook=20u?= =?UTF-8?q?seAsyncEffect?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nest_frontend/hooks/useAsyncEffect.js | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 nest_frontend/hooks/useAsyncEffect.js diff --git a/nest_frontend/hooks/useAsyncEffect.js b/nest_frontend/hooks/useAsyncEffect.js deleted file mode 100644 index 7595c3b..0000000 --- a/nest_frontend/hooks/useAsyncEffect.js +++ /dev/null @@ -1,17 +0,0 @@ -/* eslint-disable */ -import { useEffect } from "react" - - -/** - * {@link useEffect}, but with an async effect. - * - * @warning Breaks `react-hooks/exaustive-deps`. - * - * @param effect - The async effect. - * @param deps - The dependencies of the hook. - */ -export default function useAsyncEffect(effect, deps) { - useEffect(() => { - effect() - }, [effect, ...deps]) -} From 2bcfb3bdf7a710f67861d2bd9234def193fd28fb Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Sat, 22 May 2021 04:44:08 +0200 Subject: [PATCH 09/12] =?UTF-8?q?=F0=9F=92=A5=20Refactor=20more=20more=20m?= =?UTF-8?q?ore=20things?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/interactive/BoxConditionMap.js | 4 ++-- .../components/providers/RepositoryViewer.js | 4 ++-- nest_frontend/contexts/ContextLanguage.js | 2 +- .../contexts/ContextRepositoryEditor.js | 4 +++- .../contexts/ContextRepositoryViewer.js | 4 +++- nest_frontend/contexts/ContextServer.js | 2 +- nest_frontend/contexts/ContextTheme.js | 2 +- nest_frontend/contexts/ContextUser.js | 2 +- nest_frontend/contexts/README.md | 3 --- nest_frontend/hooks/useBackendRequest.js | 3 +-- nest_frontend/hooks/useLocalStorageState.js | 3 ++- nest_frontend/hooks/useMapAreaState.js | 21 +++++++++++++++++++ nest_frontend/hooks/useMapView.js | 18 ---------------- nest_frontend/hooks/useRepositoryViewer.js | 5 ++--- nest_frontend/hooks/useStrings.js | 2 +- nest_frontend/media/README.md | 3 --- nest_frontend/routes/README.md | 3 --- nest_frontend/utils/README.md | 3 --- 18 files changed, 41 insertions(+), 47 deletions(-) delete mode 100644 nest_frontend/contexts/README.md create mode 100644 nest_frontend/hooks/useMapAreaState.js delete mode 100644 nest_frontend/hooks/useMapView.js delete mode 100644 nest_frontend/media/README.md delete mode 100644 nest_frontend/routes/README.md delete mode 100644 nest_frontend/utils/README.md diff --git a/nest_frontend/components/interactive/BoxConditionMap.js b/nest_frontend/components/interactive/BoxConditionMap.js index d2a699b..3dc9ebc 100644 --- a/nest_frontend/components/interactive/BoxConditionMap.js +++ b/nest_frontend/components/interactive/BoxConditionMap.js @@ -6,7 +6,7 @@ import useRepositoryEditor from "../../hooks/useRepositoryEditor" import Condition from "../../utils/Condition" import ContextLanguage from "../../contexts/ContextLanguage" import BoxMap from "../base/BoxMap" -import useMapView from "../../hooks/useMapView" +import useMapAreaState from "../../hooks/useMapAreaState" import osmZoomLevels from "../../utils/osmZoomLevels" @@ -18,7 +18,7 @@ import osmZoomLevels from "../../utils/osmZoomLevels" * @constructor */ export default function BoxConditionMap({ ...props }) { - const mapViewHook = useMapView() + const mapViewHook = useMapAreaState() const { addCondition } = useRepositoryEditor() const { strings } = useContext(ContextLanguage) diff --git a/nest_frontend/components/providers/RepositoryViewer.js b/nest_frontend/components/providers/RepositoryViewer.js index f2dff8d..b8ad41f 100644 --- a/nest_frontend/components/providers/RepositoryViewer.js +++ b/nest_frontend/components/providers/RepositoryViewer.js @@ -24,7 +24,7 @@ import BoxFilterContains from "../interactive/BoxFilterContains" import BoxFilterUser from "../interactive/BoxFilterUser" import BoxFilterHashtag from "../interactive/BoxFilterHashtag" import BoxFilterLocation from "../interactive/BoxFilterLocation" -import useMapView from "../../hooks/useMapView" +import useMapAreaState from "../../hooks/useMapAreaState" import BoxFilterDatetime from "../interactive/BoxFilterDatetime" import BoxFilterHasPlace from "../interactive/BoxFilterHasPlace" @@ -44,7 +44,7 @@ export default function RepositoryViewer({ id, className, ...props }) { } = useArrayState([]) // FIXME: this has a severe performance impact, investigate - const mapViewHook = useMapView() + const mapViewHook = useMapAreaState() // Repository const repositoryBr = useBackendResource( diff --git a/nest_frontend/contexts/ContextLanguage.js b/nest_frontend/contexts/ContextLanguage.js index a48fb29..0b55bb4 100644 --- a/nest_frontend/contexts/ContextLanguage.js +++ b/nest_frontend/contexts/ContextLanguage.js @@ -8,7 +8,7 @@ import LocalizationStrings from "../LocalizationStrings" * - `setLang`: a function to change the current language * - `strings`: an object containing all strings of the current language * - * Defaults to Italian. + * Defaults to Italian `it`. */ export default createContext({ lang: "it", diff --git a/nest_frontend/contexts/ContextRepositoryEditor.js b/nest_frontend/contexts/ContextRepositoryEditor.js index c8db99b..3364c6b 100644 --- a/nest_frontend/contexts/ContextRepositoryEditor.js +++ b/nest_frontend/contexts/ContextRepositoryEditor.js @@ -2,6 +2,8 @@ import { createContext } from "react" /** - * Context to quickly pass props to the children of {@link RepositoryEditor}. + * React Context representing containing all variables of a {@link RepositoryEditor}. + * + * It is `null` outside a RepositoryEditor. */ export default createContext(null) diff --git a/nest_frontend/contexts/ContextRepositoryViewer.js b/nest_frontend/contexts/ContextRepositoryViewer.js index a425822..17e387b 100644 --- a/nest_frontend/contexts/ContextRepositoryViewer.js +++ b/nest_frontend/contexts/ContextRepositoryViewer.js @@ -2,6 +2,8 @@ import { createContext } from "react" /** - * Context to quickly pass props to the children of {@link RepositoryViewer}. + * React Context representing containing all variables of a {@link RepositoryViewer}. + * + * It is `null` outside a RepositoryViewer. */ export default createContext(null) diff --git a/nest_frontend/contexts/ContextServer.js b/nest_frontend/contexts/ContextServer.js index c9a8402..7623b76 100644 --- a/nest_frontend/contexts/ContextServer.js +++ b/nest_frontend/contexts/ContextServer.js @@ -2,7 +2,7 @@ import { createContext } from "react" /** - * A context containing an object with the following values: + * A React Context containing an object with the following values: * - `server`: the base URL of the currently active backend server * - `setServer`: a function to change `server` * - `fetchData`: a function to fetch JSON data from the backend server diff --git a/nest_frontend/contexts/ContextTheme.js b/nest_frontend/contexts/ContextTheme.js index 85f9a6b..bd5d07c 100644 --- a/nest_frontend/contexts/ContextTheme.js +++ b/nest_frontend/contexts/ContextTheme.js @@ -2,7 +2,7 @@ import { createContext } from "react" /** - * A context containing an object with the following elements: + * A React Context containing an object with the following elements: * - `theme` - A string containing the name of the current theme. * - `setTheme` - A function that allows changing the `theme`. * diff --git a/nest_frontend/contexts/ContextUser.js b/nest_frontend/contexts/ContextUser.js index 9a883c5..5174353 100644 --- a/nest_frontend/contexts/ContextUser.js +++ b/nest_frontend/contexts/ContextUser.js @@ -2,7 +2,7 @@ import { createContext } from "react" /** - * A context containing an object with the following values: + * A React Context containing an object with the following values: * - `user`: an object containing data about the currently logged in user * - `login`: a function accepting `email, password` as parameters which tries to login the user * - `logout`: a function accepting no parameters which logs the user out diff --git a/nest_frontend/contexts/README.md b/nest_frontend/contexts/README.md deleted file mode 100644 index 85f071e..0000000 --- a/nest_frontend/contexts/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Contexts - -In questa cartella sono contenuti i `Context` globali di React. diff --git a/nest_frontend/hooks/useBackendRequest.js b/nest_frontend/hooks/useBackendRequest.js index 9c85044..1ee6489 100644 --- a/nest_frontend/hooks/useBackendRequest.js +++ b/nest_frontend/hooks/useBackendRequest.js @@ -1,7 +1,6 @@ import { useCallback, useContext, useState } from "react" import ContextServer from "../contexts/ContextServer" import ContextUser from "../contexts/ContextUser" -import makeURLSearchParams from "../utils/makeURLSearchParams" /** @@ -45,7 +44,7 @@ export default function useBackendRequest() { // Use the body param as either search parameter or request body if(body) { if(["GET", "HEAD"].includes(method.toUpperCase())) { - path += makeURLSearchParams(body).toString() + path += URLSearchParams.fromSerializableObject(body).toString() } else { init["body"] = JSON.stringify(body) diff --git a/nest_frontend/hooks/useLocalStorageState.js b/nest_frontend/hooks/useLocalStorageState.js index 66f5be0..4c30613 100644 --- a/nest_frontend/hooks/useLocalStorageState.js +++ b/nest_frontend/hooks/useLocalStorageState.js @@ -2,7 +2,8 @@ import { useCallback, useState } from "react" /** - * Hook with the same API as {@link React.useState} which stores its value in the browser's {@link localStorage}. + * Hook with the same API as {@link React.useState} which additionally stores its value in the browser's + * {@link localStorage}. */ export default function useLocalStorageState(key, def) { /** diff --git a/nest_frontend/hooks/useMapAreaState.js b/nest_frontend/hooks/useMapAreaState.js new file mode 100644 index 0000000..f1b8354 --- /dev/null +++ b/nest_frontend/hooks/useMapAreaState.js @@ -0,0 +1,21 @@ +import { useState } from "react" +import Coordinates from "../objects/Coordinates" +import MapArea from "../objects/MapArea" + + +/** + * Hook which holds values required to create a {@link MapArea}. + */ +export default function useMapAreaState() { + const [zoom, setZoom] = useState(3) + const [center, setCenter] = useState(new Coordinates(0, 0)) + const mapArea = MapArea.fromZoomLevel(zoom, center) + + return { + zoom, + setZoom, + center, + setCenter, + mapArea, + } +} \ No newline at end of file diff --git a/nest_frontend/hooks/useMapView.js b/nest_frontend/hooks/useMapView.js deleted file mode 100644 index 990a931..0000000 --- a/nest_frontend/hooks/useMapView.js +++ /dev/null @@ -1,18 +0,0 @@ -import { useState } from "react" -import { DEFAULT_MAP_CENTER, DEFAULT_MAP_ZOOM } from "../utils/defaultMapLocation" -import osmZoomLevels from "../utils/osmZoomLevels" - - -export default function useMapView() { - const [center, setCenter] = useState(DEFAULT_MAP_CENTER) - const [zoom, setZoom] = useState(DEFAULT_MAP_ZOOM) - const radius = osmZoomLevels[zoom] - - return { - center, - setCenter, - zoom, - setZoom, - radius, - } -} \ No newline at end of file diff --git a/nest_frontend/hooks/useRepositoryViewer.js b/nest_frontend/hooks/useRepositoryViewer.js index 20e84f5..f4ae358 100644 --- a/nest_frontend/hooks/useRepositoryViewer.js +++ b/nest_frontend/hooks/useRepositoryViewer.js @@ -1,10 +1,9 @@ import { useContext } from "react" -import ContextRepositoryEditor from "../contexts/ContextRepositoryEditor" import ContextRepositoryViewer from "../contexts/ContextRepositoryViewer" /** - * Hook to quickly use {@link ContextRepositoryEditor}. + * Hook to quickly use {@link ContextRepositoryViewer}. */ export default function useRepositoryViewer() { const context = useContext(ContextRepositoryViewer) @@ -12,4 +11,4 @@ export default function useRepositoryViewer() { throw new Error("This component must be placed inside a RepositoryViewer.") } return context -} \ No newline at end of file +} diff --git a/nest_frontend/hooks/useStrings.js b/nest_frontend/hooks/useStrings.js index 4a02c4a..ce3618d 100644 --- a/nest_frontend/hooks/useStrings.js +++ b/nest_frontend/hooks/useStrings.js @@ -3,7 +3,7 @@ import ContextLanguage from "../contexts/ContextLanguage" /** - * Hook to quickly use the strings of {@link ContextLanguage}. + * Hook to quickly use the `strings` attribute of {@link ContextLanguage}. */ export default function useStrings() { return useContext(ContextLanguage).strings diff --git a/nest_frontend/media/README.md b/nest_frontend/media/README.md deleted file mode 100644 index 7a5a05a..0000000 --- a/nest_frontend/media/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Media - -In questa cartella sono contenute le immagini statiche del sito web. \ No newline at end of file diff --git a/nest_frontend/routes/README.md b/nest_frontend/routes/README.md deleted file mode 100644 index 3ae1b2c..0000000 --- a/nest_frontend/routes/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Routes - -In questa cartella sono contenuti i `Component` che vengono renderati come pagine intere. diff --git a/nest_frontend/utils/README.md b/nest_frontend/utils/README.md deleted file mode 100644 index a34cb5a..0000000 --- a/nest_frontend/utils/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Utils - -In questa cartella sono contenute alcune funzioni di utility per il sito. From 850adb5ba583fc0eda22e3176b4c2159546c83ef Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Sat, 22 May 2021 04:57:38 +0200 Subject: [PATCH 10/12] =?UTF-8?q?=F0=9F=92=A5=20Refactor=20more=20more=20m?= =?UTF-8?q?ore=20more=20things?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/interactive/BadgeCondition.js | 55 ++----------------- .../interactive/BadgeCondition.module.css | 38 ------------- .../components/interactive/BadgeFilter.js | 9 +-- .../interactive/BoxConditionDatetime.js | 21 +++---- .../BoxConditionDatetime.module.css | 7 --- .../interactive/BoxConditionHashtag.js | 15 ++--- ...onditionMap.js => BoxConditionLocation.js} | 21 +++---- .../interactive/BoxConditionUser.js | 15 ++--- .../components/interactive/BoxConditions.js | 2 +- .../interactive/FormInlineBADatetime.js | 6 +- .../components/providers/GlobalServer.js | 1 + .../components/providers/GlobalUser.js | 12 ++-- .../components/providers/RepositoryEditor.js | 4 +- 13 files changed, 52 insertions(+), 154 deletions(-) delete mode 100644 nest_frontend/components/interactive/BadgeCondition.module.css delete mode 100644 nest_frontend/components/interactive/BoxConditionDatetime.module.css rename nest_frontend/components/interactive/{BoxConditionMap.js => BoxConditionLocation.js} (65%) diff --git a/nest_frontend/components/interactive/BadgeCondition.js b/nest_frontend/components/interactive/BadgeCondition.js index 63a01fa..dc30660 100644 --- a/nest_frontend/components/interactive/BadgeCondition.js +++ b/nest_frontend/components/interactive/BadgeCondition.js @@ -1,65 +1,22 @@ import React, { useContext } from "react" -import { faAt, faClock, faGlobe, faHashtag, faMapPin } from "@fortawesome/free-solid-svg-icons" import ContextRepositoryEditor from "../../contexts/ContextRepositoryEditor" import Badge from "../base/Badge" -const CONDITION_COLORS = { - 0: "Grey", // Hashtag - 2: "Yellow", // Time - 3: "Red", // Coordinates - 4: "Red", // Place - 5: "Green", // User -} - - -const CONDITION_ICONS = { - 0: faHashtag, // Hashtag - 2: faClock, // Time - 3: faGlobe, // Coordinates - 4: faMapPin, // Place - 5: faAt, // User -} - - /** - * A {@link Badge} representing a Condition for a filter. + * A {@link Badge} representing a {@link Condition}. * - * @param condition - The Condition that this badge represents. + * @param condition - The {@link Condition} that this badge represents. * @returns {JSX.Element} * @constructor */ -export default function BadgeCondition({ ...condition }) { - const { id, type, content } = condition - const color = CONDITION_COLORS[type] - const icon = CONDITION_ICONS[type] +export default function BadgeCondition({ condition }) { const { removeRawCondition } = useContext(ContextRepositoryEditor) - let displayedContent = content - if(type === 3) { - let split = displayedContent.split(" ") - let radius = Number.parseFloat(split[1]).toFixed(0) - let radiusType = "m" - if(radius >= 2000) { - radius = Math.round(radius / 1000) - radiusType = "km" - } - let lat = Number(split[2]).toFixed(3) - let lng = Number(split[3]).toFixed(3) - displayedContent = `${split[0]} ${radius}${radiusType} ${lat} ${lng}` - } - return ( { - console.debug(`Removing Condition: `, condition) - removeRawCondition(condition) - }} - > - {displayedContent} - + {...condition.display()} + onClickDelete={() => removeRawCondition(condition)} + /> ) } diff --git a/nest_frontend/components/interactive/BadgeCondition.module.css b/nest_frontend/components/interactive/BadgeCondition.module.css deleted file mode 100644 index b67445c..0000000 --- a/nest_frontend/components/interactive/BadgeCondition.module.css +++ /dev/null @@ -1,38 +0,0 @@ -.ConditionBadge { - display: inline-flex; - - gap: 5px; - padding: 0 5px; - border-radius: 25px; - margin: 0 2px; -} - -.ConditionBadgeRed { - background-color: var(--bg-red); - color: var(--fg-red) -} - -.ConditionBadgeYellow { - background-color: var(--bg-yellow); - color: var(--fg-yellow) -} - -.ConditionBadgeGrey { - background-color: var(--bg-grey); - color: var(--fg-grey) -} - -.ConditionBadgeGreen { - background-color: var(--bg-green); - color: var(--fg-green) -} - -.Text { - max-width: 350px; - overflow-x: hidden; -} - -.Icon { - width: 15px; - text-align: center; -} \ No newline at end of file diff --git a/nest_frontend/components/interactive/BadgeFilter.js b/nest_frontend/components/interactive/BadgeFilter.js index c031b61..d3571ed 100644 --- a/nest_frontend/components/interactive/BadgeFilter.js +++ b/nest_frontend/components/interactive/BadgeFilter.js @@ -1,6 +1,6 @@ import React, { useContext } from "react" -import Badge from "../base/Badge" import ContextRepositoryViewer from "../../contexts/ContextRepositoryViewer" +import Badge from "../base/Badge" /** @@ -15,11 +15,8 @@ export default function BadgeFilter({ filter }) { return ( removeFilter(filter)} - > - {filter.text()} - + /> ) } diff --git a/nest_frontend/components/interactive/BoxConditionDatetime.js b/nest_frontend/components/interactive/BoxConditionDatetime.js index 455a0b7..1145576 100644 --- a/nest_frontend/components/interactive/BoxConditionDatetime.js +++ b/nest_frontend/components/interactive/BoxConditionDatetime.js @@ -1,17 +1,16 @@ -import React, { useContext } from "react" +import React, { useCallback, useContext } from "react" import BoxFull from "../base/BoxFull" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" import { faClock } from "@fortawesome/free-solid-svg-icons" import useRepositoryEditor from "../../hooks/useRepositoryEditor" -import Condition from "../../utils/Condition" -import convertToLocalISODate from "../../utils/convertToLocalISODate" import ContextLanguage from "../../contexts/ContextLanguage" import FormInlineBADatetime from "./FormInlineBADatetime" +import { ConditionTime } from "../../objects/Condition" /** - * A {@link BoxFull} that allows the user to select a Twitter user to search for, and then to add it as a Condition - * to the {@link ContextRepositoryEditor}. + * A {@link BoxFull} that allows the user to select a Twitter user to search for, and then to add it as a + * {@link ConditionTime} of a RepositoryEditor. * * @param props - Additional props to pass to the box. * @returns {JSX.Element} @@ -21,14 +20,10 @@ export default function BoxConditionDatetime({ ...props }) { const { addCondition } = useRepositoryEditor() const { strings } = useContext(ContextLanguage) - const submit = ({ date, isBefore }) => { - if(date.toString() === "Invalid Date") { - console.debug("Refusing to add condition: ", date, " is an Invalid Date.") - return - } - const aware = convertToLocalISODate(date) - addCondition(new Condition("TIME", `${isBefore ? "<" : ">"} ${aware}`)) - } + const submit = useCallback( + timeRay => addCondition(new ConditionTime(timeRay)), + [addCondition] + ) return ( { - addCondition(new Condition("HASHTAG", value)) - } + const submit = useCallback( + value => addCondition(new ConditionHashtag(value)), + [addCondition] + ) return ( { - const radius = mapViewHook.zoom * osmZoomLevels[mapViewHook.zoom] - - addCondition(new Condition( - "COORDINATES", - `< ${radius} ${mapViewHook.center.lat} ${mapViewHook.center.lng}`, - )) - }, + () => addCondition(new ConditionLocation(mapViewHook.mapArea)), [mapViewHook, addCondition] ) @@ -41,7 +34,7 @@ export default function BoxConditionMap({ ...props }) { {strings.searchBy}   - +   {strings.byZone} diff --git a/nest_frontend/components/interactive/BoxConditionUser.js b/nest_frontend/components/interactive/BoxConditionUser.js index f7dced1..00bddd9 100644 --- a/nest_frontend/components/interactive/BoxConditionUser.js +++ b/nest_frontend/components/interactive/BoxConditionUser.js @@ -1,16 +1,16 @@ -import React, { useContext } from "react" +import React, { useCallback, useContext } from "react" import BoxFull from "../base/BoxFull" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" import { faAt } from "@fortawesome/free-solid-svg-icons" import useRepositoryEditor from "../../hooks/useRepositoryEditor" -import Condition from "../../utils/Condition" import ContextLanguage from "../../contexts/ContextLanguage" import FormInlineUser from "./FormInlineUser" +import { ConditionHashtag, ConditionUser } from "../../objects/Condition" /** - * A {@link BoxFull} that allows the user to select a Twitter user to search for, and then to add it as a Condition - * to the {@link ContextRepositoryEditor}. + * A {@link BoxFull} that allows the user to select a Twitter user to search for, and then to add it as a + * {@link ConditionUser} of a RepositoryEditor. * * @param props - Additional props to pass to the box. * @returns {JSX.Element} @@ -20,9 +20,10 @@ export default function BoxConditionUser({ ...props }) { const { addCondition } = useRepositoryEditor() const { strings } = useContext(ContextLanguage) - const submit = value => { - addCondition(new Condition("USER", value)) - } + const submit = useCallback( + value => addCondition(new ConditionUser(value)), + [addCondition] + ) return ( ) + const badges = conditions.map((cond, pos) => ) return ( diff --git a/nest_frontend/components/interactive/FormInlineBADatetime.js b/nest_frontend/components/interactive/FormInlineBADatetime.js index 1e4055c..ccf88dc 100644 --- a/nest_frontend/components/interactive/FormInlineBADatetime.js +++ b/nest_frontend/components/interactive/FormInlineBADatetime.js @@ -5,6 +5,7 @@ import { faClock, faPlus } from "@fortawesome/free-solid-svg-icons" import ButtonIconOnly from "../base/ButtonIconOnly" import Style from "./FormInlineText.module.css" import ButtonToggleBeforeAfter from "./ButtonToggleBeforeAfter" +import TimeRay from "../../objects/TimeRay" const INVALID_CHARACTERS = /[^0-9TZ:+-]/g @@ -26,10 +27,7 @@ export default function FormInlineBADatetime( const _onSubmit = event => { event.preventDefault() - submit({ - date: new Date(value), - isBefore, - }) + submit(new TimeRay(isBefore, new Date(value))) setValue("") } diff --git a/nest_frontend/components/providers/GlobalServer.js b/nest_frontend/components/providers/GlobalServer.js index 69a2346..0250e99 100644 --- a/nest_frontend/components/providers/GlobalServer.js +++ b/nest_frontend/components/providers/GlobalServer.js @@ -14,6 +14,7 @@ import isString from "is-string" * @constructor */ export default function GlobalServer({ children }) { + // TODO: Set this using an envvar const [server, setServer] = useLocalStorageState("server", "http://127.0.0.1:5000") /** diff --git a/nest_frontend/components/providers/GlobalUser.js b/nest_frontend/components/providers/GlobalUser.js index 8bbe256..8c2d798 100644 --- a/nest_frontend/components/providers/GlobalUser.js +++ b/nest_frontend/components/providers/GlobalUser.js @@ -50,13 +50,13 @@ export default function GlobalUser({ children }) { * @returns {Promise} */ const login = useCallback(async (email, password) => { - console.debug("Contattando il server per accedere...") + console.debug("Contacting the server to login...") const data = await fetchData("POST", `/api/v1/login`, { "email": email, "password": password, }) - console.debug("Memorizzando lo stato di login...") + console.debug("Saving login state...") setUser({ email: data["user"]["email"], isAdmin: data["user"]["isAdmin"], @@ -64,18 +64,18 @@ export default function GlobalUser({ children }) { token: data["access_token"], }) - console.info("Accesso effettuato!") + console.info("Login successful!") }, [fetchData, setUser]) /** * Logout from the currently active server. */ const logout = useCallback(() => { - console.debug("Ripulendo lo stato di login...") + console.debug("Clearing login state...") setUser(null) - console.debug("Stato di login ripulito!") + console.debug("Cleared login state!") - console.info("Logout avvenuto con successo!") + console.info("Logout successful!") }, [setUser]) return ( diff --git a/nest_frontend/components/providers/RepositoryEditor.js b/nest_frontend/components/providers/RepositoryEditor.js index 248b00a..f933204 100644 --- a/nest_frontend/components/providers/RepositoryEditor.js +++ b/nest_frontend/components/providers/RepositoryEditor.js @@ -2,7 +2,7 @@ import React, { useCallback, useContext, useMemo, useState } from "react" import ContextRepositoryEditor from "../../contexts/ContextRepositoryEditor" import useArrayState from "../../hooks/useArrayState" import Style from "./RepositoryEditor.module.css" -import BoxConditionMap from "../interactive/BoxConditionMap" +import BoxConditionLocation from "../interactive/BoxConditionLocation" import BoxConditionHashtag from "../interactive/BoxConditionHashtag" import BoxConditionUser from "../interactive/BoxConditionUser" import BoxConditionDatetime from "../interactive/BoxConditionDatetime" @@ -142,7 +142,7 @@ export default function RepositoryEditor({ }} >
- + From ac1a218677d8f8376a2cc5f2271f69c119b6b9cf Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Sun, 23 May 2021 05:03:41 +0200 Subject: [PATCH 11/12] =?UTF-8?q?=F0=9F=93=94=20Document=20a=20lot=20of=20?= =?UTF-8?q?stuff?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../interactive/BoxConditionDatetime.js | 4 ++-- .../interactive/BoxFilterContains.js | 12 +++++++++-- .../interactive/BoxFilterDatetime.js | 20 +++++++++++++------ .../interactive/BoxFilterHasPlace.js | 11 ++++++++-- .../interactive/BoxFilterHashtag.js | 12 +++++++++-- .../interactive/BoxFilterLocation.js | 12 +++++++++-- .../components/interactive/BoxFilterUser.js | 14 +++++++++---- .../components/interactive/BoxFilters.js | 1 + .../components/interactive/BoxLoggedIn.js | 2 +- .../BoxRepositoryCreate.module.css | 3 --- .../interactive/BoxRepositoryTweets.js | 18 ++++++++++++----- .../components/interactive/BoxUserCreate.js | 9 +++++++++ .../components/interactive/BoxUserList.js | 10 ++++++++++ .../interactive/BoxVisualizationMap.js | 9 ++++++++- .../interactive/BoxVisualizationWordcloud.js | 13 +++++++----- .../components/interactive/ButtonPicker.js | 10 ++++++++++ .../interactive/ButtonToggleBeforeAfter.js | 10 ++++++++++ nest_frontend/components/interactive/Empty.js | 10 +++++++++- .../interactive/FormInlineHashtag.js | 8 ++++++++ .../interactive/FormInlineLocation.js | 15 ++++++++++++++ .../components/interactive/FormInlineText.js | 13 ++++++++++++ ...lineBADatetime.js => FormInlineTimeRay.js} | 15 +++++++++++++- .../components/interactive/FormInlineUser.js | 10 ++++++++-- .../components/interactive/PickerFilter.js | 7 +++++++ .../interactive/PickerVisualization.js | 7 +++++++ .../components/interactive/SummaryTweet.js | 8 ++++++++ .../components/interactive/SummaryUser.js | 10 ++++++++++ 27 files changed, 234 insertions(+), 39 deletions(-) delete mode 100644 nest_frontend/components/interactive/BoxRepositoryCreate.module.css rename nest_frontend/components/interactive/{FormInlineBADatetime.js => FormInlineTimeRay.js} (72%) diff --git a/nest_frontend/components/interactive/BoxConditionDatetime.js b/nest_frontend/components/interactive/BoxConditionDatetime.js index 1145576..2b11fe9 100644 --- a/nest_frontend/components/interactive/BoxConditionDatetime.js +++ b/nest_frontend/components/interactive/BoxConditionDatetime.js @@ -4,7 +4,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" import { faClock } from "@fortawesome/free-solid-svg-icons" import useRepositoryEditor from "../../hooks/useRepositoryEditor" import ContextLanguage from "../../contexts/ContextLanguage" -import FormInlineBADatetime from "./FormInlineBADatetime" +import FormInlineTimeRay from "./FormInlineTimeRay" import { ConditionTime } from "../../objects/Condition" @@ -38,7 +38,7 @@ export default function BoxConditionDatetime({ ...props }) { } {...props} > - diff --git a/nest_frontend/components/interactive/BoxFilterContains.js b/nest_frontend/components/interactive/BoxFilterContains.js index 545d263..b76ea85 100644 --- a/nest_frontend/components/interactive/BoxFilterContains.js +++ b/nest_frontend/components/interactive/BoxFilterContains.js @@ -2,18 +2,26 @@ import React from "react" import BoxFull from "../base/BoxFull" import useRepositoryViewer from "../../hooks/useRepositoryViewer" import useStrings from "../../hooks/useStrings" -import { FilterContains } from "../../utils/Filter" +import { FilterContains } from "../../objects/Filter" import FormInlineText from "./FormInlineText" import { faFont } from "@fortawesome/free-solid-svg-icons" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" +/** + * A {@link BoxFull} that allows the user to select a word to search for, and then to add it as a + * {@link FilterContains} of a RepositoryViewer. + * + * @param props - Additional props to pass to the box. + * @returns {JSX.Element} + * @constructor + */ export default function BoxFilterContains({ ...props }) { const strings = useStrings() const { appendFilter } = useRepositoryViewer() const submit = value => { - appendFilter(new FilterContains(false, value)) + appendFilter(new FilterContains(value)) } // TODO: add this string diff --git a/nest_frontend/components/interactive/BoxFilterDatetime.js b/nest_frontend/components/interactive/BoxFilterDatetime.js index ab0c1ff..47743da 100644 --- a/nest_frontend/components/interactive/BoxFilterDatetime.js +++ b/nest_frontend/components/interactive/BoxFilterDatetime.js @@ -1,19 +1,27 @@ import React from "react" import BoxFull from "../base/BoxFull" -import { faClock, faHashtag } from "@fortawesome/free-solid-svg-icons" +import { faClock } from "@fortawesome/free-solid-svg-icons" import useRepositoryViewer from "../../hooks/useRepositoryViewer" import useStrings from "../../hooks/useStrings" -import { FilterInsideTimeRay } from "../../utils/Filter" +import { FilterInsideTimeRay } from "../../objects/Filter" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" -import FormInlineBADatetime from "./FormInlineBADatetime" +import FormInlineTimeRay from "./FormInlineTimeRay" +/** + * A {@link BoxFull} that allows the user to select a {@link TimeRay}, and then to add it as a + * {@link FilterInsideTimeRay} of a RepositoryViewer. + * + * @param props - Additional props to pass to the box. + * @returns {JSX.Element} + * @constructor + */ export default function BoxFilterDatetime({ ...props }) { const strings = useStrings() const { appendFilter } = useRepositoryViewer() - const submit = ({ date, isBefore }) => { - appendFilter(new FilterInsideTimeRay(isBefore, date)) + const submit = ({ timeRay }) => { + appendFilter(new FilterInsideTimeRay(timeRay)) } return ( @@ -29,7 +37,7 @@ export default function BoxFilterDatetime({ ...props }) { } {...props} > - + ) } diff --git a/nest_frontend/components/interactive/BoxFilterHasPlace.js b/nest_frontend/components/interactive/BoxFilterHasPlace.js index ffaa711..49c4d7a 100644 --- a/nest_frontend/components/interactive/BoxFilterHasPlace.js +++ b/nest_frontend/components/interactive/BoxFilterHasPlace.js @@ -5,17 +5,24 @@ import useRepositoryViewer from "../../hooks/useRepositoryViewer" import useStrings from "../../hooks/useStrings" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" import { faLocationArrow, faPlus } from "@fortawesome/free-solid-svg-icons" -import { FilterWithPlace } from "../../utils/Filter" +import { FilterWithPlace } from "../../objects/Filter" import ButtonIconOnly from "../base/ButtonIconOnly" +/** + * A {@link BoxFull} that allows the user to add a {@link FilterWithPlace} to a RepositoryViewer. + * + * @param props - Additional props to pass to the box. + * @returns {JSX.Element} + * @constructor + */ export default function BoxFilterHasPlace({ ...props }) { const strings = useStrings() const { appendFilter } = useRepositoryViewer() const submit = () => { - appendFilter(new FilterWithPlace(false)) + appendFilter(new FilterWithPlace()) } // TODO: translate this diff --git a/nest_frontend/components/interactive/BoxFilterHashtag.js b/nest_frontend/components/interactive/BoxFilterHashtag.js index 211d461..b1f5145 100644 --- a/nest_frontend/components/interactive/BoxFilterHashtag.js +++ b/nest_frontend/components/interactive/BoxFilterHashtag.js @@ -3,17 +3,25 @@ import BoxFull from "../base/BoxFull" import { faClock } from "@fortawesome/free-solid-svg-icons" import useRepositoryViewer from "../../hooks/useRepositoryViewer" import useStrings from "../../hooks/useStrings" -import { FilterHashtag } from "../../utils/Filter" +import { FilterHashtag } from "../../objects/Filter" import FormInlineHashtag from "./FormInlineHashtag" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" +/** + * A {@link BoxFull} that allows the user to select a Twitter hashtag to search for, and then to add it as a + * {@link FilterContains} of a RepositoryViewer. + * + * @param props - Additional props to pass to the box. + * @returns {JSX.Element} + * @constructor + */ export default function BoxFilterHashtag({ ...props }) { const strings = useStrings() const { appendFilter } = useRepositoryViewer() const submit = value => { - appendFilter(new FilterHashtag(false, value)) + appendFilter(new FilterHashtag(value)) } return ( diff --git a/nest_frontend/components/interactive/BoxFilterLocation.js b/nest_frontend/components/interactive/BoxFilterLocation.js index 5c036f7..c7d7114 100644 --- a/nest_frontend/components/interactive/BoxFilterLocation.js +++ b/nest_frontend/components/interactive/BoxFilterLocation.js @@ -1,14 +1,22 @@ import React from "react" import BoxFull from "../base/BoxFull" -import FormInline from "../base/FormInline" import useRepositoryViewer from "../../hooks/useRepositoryViewer" import useStrings from "../../hooks/useStrings" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" import { faMapPin } from "@fortawesome/free-solid-svg-icons" import FormInlineLocation from "./FormInlineLocation" -import { FilterInsideMapArea } from "../../utils/Filter" +import { FilterInsideMapArea } from "../../objects/Filter" +/** + * A {@link BoxFull} that allows the user to add a {@link FilterInsideMapArea} to a RepositoryViewer. + * + * It connects to the `mapViewHook` of the RepositoryViewer. + * + * @param props - Additional props to pass to the box. + * @returns {JSX.Element} + * @constructor + */ export default function BoxFilterLocation({ ...props }) { const strings = useStrings() diff --git a/nest_frontend/components/interactive/BoxFilterUser.js b/nest_frontend/components/interactive/BoxFilterUser.js index edcb702..2c7340d 100644 --- a/nest_frontend/components/interactive/BoxFilterUser.js +++ b/nest_frontend/components/interactive/BoxFilterUser.js @@ -3,20 +3,26 @@ import BoxFull from "../base/BoxFull" import { faAt } from "@fortawesome/free-solid-svg-icons" import useRepositoryViewer from "../../hooks/useRepositoryViewer" import useStrings from "../../hooks/useStrings" -import { FilterPoster } from "../../utils/Filter" +import { FilterPoster } from "../../objects/Filter" import FormInlineUser from "./FormInlineUser" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" +/** + * A {@link BoxFull} that allows the user to select a Twitter user to search for, and then to add it as a + * {@link FilterPoster} of a RepositoryViewer. + * + * @param props - Additional props to pass to the box. + * @returns {JSX.Element} + * @constructor + */ export default function BoxFilterUser({ ...props }) { - // TODO: Translate this - // TODO: and also use a better string maybe const strings = useStrings() const { appendFilter } = useRepositoryViewer() const submit = value => { - appendFilter(new FilterPoster(false, value)) + appendFilter(new FilterPoster(value)) } return ( diff --git a/nest_frontend/components/interactive/BoxFilters.js b/nest_frontend/components/interactive/BoxFilters.js index 0745124..382a727 100644 --- a/nest_frontend/components/interactive/BoxFilters.js +++ b/nest_frontend/components/interactive/BoxFilters.js @@ -19,6 +19,7 @@ export default function BoxFilters({ ...props }) { const badges = filters.map((filter, pos) => ) // TODO: localize this + return ( {badges} diff --git a/nest_frontend/components/interactive/BoxLoggedIn.js b/nest_frontend/components/interactive/BoxLoggedIn.js index ad3488c..dba6e51 100644 --- a/nest_frontend/components/interactive/BoxLoggedIn.js +++ b/nest_frontend/components/interactive/BoxLoggedIn.js @@ -13,7 +13,7 @@ import ContextLanguage from "../../contexts/ContextLanguage" /** * A {@link BoxFull} displaying the user's current login status, and allowing them to logout. * - * @param props + * @param props - Additional props to pass to the box. * @returns {JSX.Element} * @constructor */ diff --git a/nest_frontend/components/interactive/BoxRepositoryCreate.module.css b/nest_frontend/components/interactive/BoxRepositoryCreate.module.css deleted file mode 100644 index 3ba2249..0000000 --- a/nest_frontend/components/interactive/BoxRepositoryCreate.module.css +++ /dev/null @@ -1,3 +0,0 @@ -.BoxRepositoryCreate { - -} diff --git a/nest_frontend/components/interactive/BoxRepositoryTweets.js b/nest_frontend/components/interactive/BoxRepositoryTweets.js index b03a774..822cba7 100644 --- a/nest_frontend/components/interactive/BoxRepositoryTweets.js +++ b/nest_frontend/components/interactive/BoxRepositoryTweets.js @@ -1,14 +1,22 @@ -import React, { useContext } from "react" +import React from "react" import BoxFullScrollable from "../base/BoxFullScrollable" import SummaryTweet from "./SummaryTweet" -import ContextLanguage from "../../contexts/ContextLanguage" import Empty from "./Empty" -import ContextRepositoryViewer from "../../contexts/ContextRepositoryViewer" +import useRepositoryViewer from "../../hooks/useRepositoryViewer" +import useStrings from "../../hooks/useStrings" +/** + * A {@link BoxFullScrollable} rendering all the tweets currently displayed in a RepositoryViewer as + * {@link SummaryTweet}s. + * + * @param props - Additional props to pass to the box. + * @returns {JSX.Element} + * @constructor + */ export default function BoxRepositoryTweets({ ...props }) { - const { strings } = useContext(ContextLanguage) - const { tweets } = useContext(ContextRepositoryViewer) + const strings = useStrings() + const { tweets } = useRepositoryViewer() let content if(tweets.length === 0) { diff --git a/nest_frontend/components/interactive/BoxUserCreate.js b/nest_frontend/components/interactive/BoxUserCreate.js index 5996a03..8280460 100644 --- a/nest_frontend/components/interactive/BoxUserCreate.js +++ b/nest_frontend/components/interactive/BoxUserCreate.js @@ -9,6 +9,15 @@ import FormAlert from "../base/formparts/FormAlert" import ContextLanguage from "../../contexts/ContextLanguage" +/** + * A {@link BoxFull} allowing an administrator user to create a new user. + * + * @param createUser - Async function to call to create an user. + * @param running - Whether another request is currently running. + * @param props - Additional props to pass to the box. + * @returns {JSX.Element} + * @constructor + */ export default function BoxUserCreate({ createUser, running, ...props }) { const [username, setUsername] = useState("") const [email, setEmail] = useState("") diff --git a/nest_frontend/components/interactive/BoxUserList.js b/nest_frontend/components/interactive/BoxUserList.js index db0f6a4..4f18b1b 100644 --- a/nest_frontend/components/interactive/BoxUserList.js +++ b/nest_frontend/components/interactive/BoxUserList.js @@ -5,6 +5,16 @@ import SummaryUser from "./SummaryUser" import ContextLanguage from "../../contexts/ContextLanguage" +/** + * A {@link BoxFullScrollable} rendering an array of users as {@link SummaryUser}s. + * + * @param users - Array of users to render. + * @param destroyUser - Async function to destroy an user, to be passed to {@link SummaryUser}. + * @param running - Whether another request is currently running. + * @param props - Additional props to pass to the box. + * @returns {JSX.Element} + * @constructor + */ export default function BoxUserList({ users, destroyUser, running, ...props }) { const { strings } = useContext(ContextLanguage) diff --git a/nest_frontend/components/interactive/BoxVisualizationMap.js b/nest_frontend/components/interactive/BoxVisualizationMap.js index 9e4f82e..910ff21 100644 --- a/nest_frontend/components/interactive/BoxVisualizationMap.js +++ b/nest_frontend/components/interactive/BoxVisualizationMap.js @@ -2,10 +2,17 @@ import React, { useContext, useMemo } from "react" import BoxMap from "../base/BoxMap" import ContextLanguage from "../../contexts/ContextLanguage" import { Marker, Popup } from "react-leaflet" -import { Location } from "../../utils/location" +import { Location } from "../../objects/location" import ContextRepositoryViewer from "../../contexts/ContextRepositoryViewer" +/** + * A {@link BoxMap} displaying the displayed tweets of a RepositoryViewer as {@link Marker}s. + * + * @param props - Additional props to pass to the box. + * @returns {JSX.Element} + * @constructor + */ export default function BoxVisualizationMap({ ...props }) { const { strings } = useContext(ContextLanguage) const { tweets, mapViewHook } = useContext(ContextRepositoryViewer) diff --git a/nest_frontend/components/interactive/BoxVisualizationWordcloud.js b/nest_frontend/components/interactive/BoxVisualizationWordcloud.js index be53923..2cfe97b 100644 --- a/nest_frontend/components/interactive/BoxVisualizationWordcloud.js +++ b/nest_frontend/components/interactive/BoxVisualizationWordcloud.js @@ -1,10 +1,10 @@ -import React, { useContext } from "react" +import React, { useCallback, useContext } from "react" import BoxWordcloud from "../base/BoxWordcloud" import ContextLanguage from "../../contexts/ContextLanguage" import BoxFull from "../base/BoxFull" import Empty from "./Empty" import ContextRepositoryViewer from "../../contexts/ContextRepositoryViewer" -import { FilterContains } from "../../utils/Filter" +import { FilterContains } from "../../objects/Filter" export default function BoxVisualizationWordcloud({ ...props }) { @@ -19,9 +19,12 @@ export default function BoxVisualizationWordcloud({ ...props }) { ) } - const onWordClick = word => { - appendFilter(new FilterContains(false, word.text)) - } + const onWordClick = useCallback( + word => { + appendFilter(new FilterContains(word.text)) + }, + [appendFilter] + ) return ( ` element to be used when there is nothing to be displayed inside a box. + * + * @param className - Additional class(es) to append to the element. + * @param props - Additional props to pass to the element. + * @returns {JSX.Element} + * @constructor + */ +export default function Empty({ className, ...props }) { const { strings } = useContext(ContextLanguage) return ( diff --git a/nest_frontend/components/interactive/FormInlineHashtag.js b/nest_frontend/components/interactive/FormInlineHashtag.js index a1a156f..e6753b6 100644 --- a/nest_frontend/components/interactive/FormInlineHashtag.js +++ b/nest_frontend/components/interactive/FormInlineHashtag.js @@ -8,6 +8,14 @@ import { faHashtag } from "@fortawesome/free-solid-svg-icons" const INVALID_CHARACTERS = /([^a-z0-9_\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u00ff\u0100-\u024f\u0253-\u0254\u0256-\u0257\u0300-\u036f\u1e00-\u1eff\u0400-\u04ff\u0500-\u0527\u2de0-\u2dff\ua640-\ua69f\u0591-\u05bf\u05c1-\u05c2\u05c4-\u05c5\u05d0-\u05ea\u05f0-\u05f4\ufb12-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufb4f\u0610-\u061a\u0620-\u065f\u066e-\u06d3\u06d5-\u06dc\u06de-\u06e8\u06ea-\u06ef\u06fa-\u06fc\u0750-\u077f\u08a2-\u08ac\u08e4-\u08fe\ufb50-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\u200c\u0e01-\u0e3a\u0e40-\u0e4e\u1100-\u11ff\u3130-\u3185\ua960-\ua97f\uac00-\ud7af\ud7b0-\ud7ff\uffa1-\uffdc\u30a1-\u30fa\u30fc-\u30fe\uff66-\uff9f\uff10-\uff19\uff21-\uff3a\uff41-\uff5a\u3041-\u3096\u3099-\u309e\u3400-\u4dbf\u4e00-\u9fff\u20000-\u2a6df\u2a700-\u2b73\u2b740-\u2b81\u2f800-\u2fa1])/g +/** + * A {@link FormInline} allowing the user to select a Twitter hashtag. + * + * @param submit - Function called when the submit button is pressed. + * @param props - Additional props to pass to the form. + * @returns {JSX.Element} + * @constructor + */ export default function FormInlineHashtag({ submit, ...props }) { const validate = value => { diff --git a/nest_frontend/components/interactive/FormInlineLocation.js b/nest_frontend/components/interactive/FormInlineLocation.js index eab623b..f55a6ca 100644 --- a/nest_frontend/components/interactive/FormInlineLocation.js +++ b/nest_frontend/components/interactive/FormInlineLocation.js @@ -6,6 +6,21 @@ import ButtonIconOnly from "../base/ButtonIconOnly" import Style from "./FormInlineLocation.module.css" +/** + * @deprecated to be refactored + * @param mapViewHook + * @param radIcon + * @param latIcon + * @param lngIcon + * @param buttonIcon + * @param buttonColor + * @param placeholder + * @param validate + * @param submit + * @param props + * @returns {JSX.Element} + * @constructor + */ export default function FormInlineLocation( { mapViewHook, diff --git a/nest_frontend/components/interactive/FormInlineText.js b/nest_frontend/components/interactive/FormInlineText.js index 00217f6..80c625d 100644 --- a/nest_frontend/components/interactive/FormInlineText.js +++ b/nest_frontend/components/interactive/FormInlineText.js @@ -6,6 +6,19 @@ import ButtonIconOnly from "../base/ButtonIconOnly" import Style from "./FormInlineText.module.css" +/** + * A {@link FormInline} allowing the user to enter a string. + * + * @param textIcon - The icon to display in the text field. + * @param buttonIcon - The icon to display on the submit button. + * @param buttonColor - The color of the submit button. + * @param placeholder - The placeholder of the text field. + * @param validate - Function string> called to set the value of the text field. + * @param submit - Function called when the submit button is pressed. + * @param props - Additional props to pass to the form. + * @returns {JSX.Element} + * @constructor + */ export default function FormInlineText( { textIcon = faFont, diff --git a/nest_frontend/components/interactive/FormInlineBADatetime.js b/nest_frontend/components/interactive/FormInlineTimeRay.js similarity index 72% rename from nest_frontend/components/interactive/FormInlineBADatetime.js rename to nest_frontend/components/interactive/FormInlineTimeRay.js index ccf88dc..90b5815 100644 --- a/nest_frontend/components/interactive/FormInlineBADatetime.js +++ b/nest_frontend/components/interactive/FormInlineTimeRay.js @@ -11,7 +11,20 @@ import TimeRay from "../../objects/TimeRay" const INVALID_CHARACTERS = /[^0-9TZ:+-]/g -export default function FormInlineBADatetime( +/** + * A {@link FormInline} allowing the user to select a {@link TimeRay}. + * + * @param textIcon - The icon to display in the text field. + * @param buttonIcon - The icon to display on the submit button. + * @param buttonColor - The color of the submit button. + * @param placeholder - The placeholder of the text field. + * @param validate - Function string> called to set the value of the text field. + * @param submit - Function <{@link TimeRay}> called when the submit button is pressed. + * @param props - Additional props to pass to the form. + * @returns {JSX.Element} + * @constructor + */ +export default function FormInlineTimeRay( { textIcon = faClock, buttonIcon = faPlus, diff --git a/nest_frontend/components/interactive/FormInlineUser.js b/nest_frontend/components/interactive/FormInlineUser.js index 836acfa..275e655 100644 --- a/nest_frontend/components/interactive/FormInlineUser.js +++ b/nest_frontend/components/interactive/FormInlineUser.js @@ -3,11 +3,17 @@ import FormInlineText from "./FormInlineText" import { faAt } from "@fortawesome/free-solid-svg-icons" -// Official hashtag regex from https://stackoverflow.com/a/22490853/4334568 -// noinspection RegExpAnonymousGroup,LongLine const INVALID_CHARACTERS = /[^a-zA-Z0-9]/g +/** + * A {@link FormInline} allowing the user to select a Twitter user. + * + * @param submit - Function called when the submit button is pressed. + * @param props - Additional props to pass to the form. + * @returns {JSX.Element} + * @constructor + */ export default function FormInlineUser({ submit, ...props }) { const validate = value => { diff --git a/nest_frontend/components/interactive/PickerFilter.js b/nest_frontend/components/interactive/PickerFilter.js index c44f6b4..478fe43 100644 --- a/nest_frontend/components/interactive/PickerFilter.js +++ b/nest_frontend/components/interactive/PickerFilter.js @@ -5,6 +5,13 @@ import ButtonPicker from "./ButtonPicker" import ContextRepositoryViewer from "../../contexts/ContextRepositoryViewer" +/** + * Tab selector for the Add Filter box of a RepositoryViewer. + * + * @param props - Additional props to pass to the div. + * @returns {JSX.Element} + * @constructor + */ export default function PickerFilter({ ...props }) { const { filterTab, setFilterTab, setVisualizationTab } = useContext(ContextRepositoryViewer) diff --git a/nest_frontend/components/interactive/PickerVisualization.js b/nest_frontend/components/interactive/PickerVisualization.js index 37f85e0..2bee8e7 100644 --- a/nest_frontend/components/interactive/PickerVisualization.js +++ b/nest_frontend/components/interactive/PickerVisualization.js @@ -4,6 +4,13 @@ import ContextRepositoryViewer from "../../contexts/ContextRepositoryViewer" import ButtonPicker from "./ButtonPicker" +/** + * Tab selector for the Visualization box of a RepositoryViewer. + * + * @param props - Additional props to pass to the div. + * @returns {JSX.Element} + * @constructor + */ export default function PickerVisualization({ ...props }) { const { visualizationTab, setVisualizationTab } = useContext(ContextRepositoryViewer) diff --git a/nest_frontend/components/interactive/SummaryTweet.js b/nest_frontend/components/interactive/SummaryTweet.js index b3d2809..2033d77 100644 --- a/nest_frontend/components/interactive/SummaryTweet.js +++ b/nest_frontend/components/interactive/SummaryTweet.js @@ -6,6 +6,14 @@ import SummaryText from "../base/summary/SummaryText" import SummaryRight from "../base/summary/SummaryRight" +/** + * A {@link SummaryBase} representing a tweet. + * + * @param tweet - The tweet to represent. + * @param props - Additional props to pass to the summary. + * @returns {JSX.Element} + * @constructor + */ export default function SummaryTweet({ tweet, ...props }) { let icon if(tweet["location"]) { diff --git a/nest_frontend/components/interactive/SummaryUser.js b/nest_frontend/components/interactive/SummaryUser.js index 395763b..0124205 100644 --- a/nest_frontend/components/interactive/SummaryUser.js +++ b/nest_frontend/components/interactive/SummaryUser.js @@ -8,6 +8,16 @@ import SummaryButton from "../base/summary/SummaryButton" import SummaryRight from "../base/summary/SummaryRight" +/** + * A {@link SummaryBase} representing a N.E.S.T. user. + * + * @param user - The user to represent. + * @param destroyUser - Async function to destroy an user from the frontend. + * @param running - Whether another request is already running. + * @param props - Additional props to pass to the summary. + * @returns {JSX.Element} + * @constructor + */ export default function SummaryUser({ user, destroyUser, running, ...props }) { const { strings } = useContext(ContextLanguage) From 2870a467265dc7862cdda2dcf148a422454e364e Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Sun, 23 May 2021 05:35:17 +0200 Subject: [PATCH 12/12] =?UTF-8?q?=F0=9F=92=A5=20Fix=20bugs,=20refactor=20t?= =?UTF-8?q?hings,=20document=20stuff?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nest_frontend/components/base/Button.module.css | 2 ++ .../components/interactive/BoxConditionLocation.js | 2 +- .../components/interactive/BoxFilterDatetime.js | 2 +- .../components/interactive/BoxFilterHasPlace.js | 4 ++-- .../components/interactive/BoxFilterHashtag.js | 6 +++--- .../components/interactive/BoxFilterLocation.js | 7 ++++--- .../components/interactive/BoxVisualizationMap.js | 8 +++++--- .../components/interactive/FormInlineHashtag.js | 4 +--- .../components/interactive/FormInlineLocation.js | 6 +++--- .../components/interactive/FormInlineText.js | 2 ++ .../components/interactive/FormInlineTimeRay.js | 7 +++++-- .../components/interactive/FormInlineUser.js | 4 +--- .../components/interactive/PickerFilter.js | 14 +++++++++++--- nest_frontend/objects/Coordinates.js | 14 +++++++++++++- nest_frontend/objects/Filter.js | 6 +++--- 15 files changed, 57 insertions(+), 31 deletions(-) diff --git a/nest_frontend/components/base/Button.module.css b/nest_frontend/components/base/Button.module.css index 134d88f..e8e3f9a 100644 --- a/nest_frontend/components/base/Button.module.css +++ b/nest_frontend/components/base/Button.module.css @@ -13,6 +13,8 @@ .Button[disabled] { opacity: 0.5; + + cursor: not-allowed; } .Button:focus-visible { diff --git a/nest_frontend/components/interactive/BoxConditionLocation.js b/nest_frontend/components/interactive/BoxConditionLocation.js index f9b3706..10f426c 100644 --- a/nest_frontend/components/interactive/BoxConditionLocation.js +++ b/nest_frontend/components/interactive/BoxConditionLocation.js @@ -1,6 +1,6 @@ import React, { useCallback, useContext } from "react" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" -import { faLocationArrow, faMapPin, faPlus } from "@fortawesome/free-solid-svg-icons" +import { faLocationArrow, faPlus } from "@fortawesome/free-solid-svg-icons" import ButtonIconOnly from "../base/ButtonIconOnly" import useRepositoryEditor from "../../hooks/useRepositoryEditor" import ContextLanguage from "../../contexts/ContextLanguage" diff --git a/nest_frontend/components/interactive/BoxFilterDatetime.js b/nest_frontend/components/interactive/BoxFilterDatetime.js index 47743da..5e02718 100644 --- a/nest_frontend/components/interactive/BoxFilterDatetime.js +++ b/nest_frontend/components/interactive/BoxFilterDatetime.js @@ -20,7 +20,7 @@ export default function BoxFilterDatetime({ ...props }) { const strings = useStrings() const { appendFilter } = useRepositoryViewer() - const submit = ({ timeRay }) => { + const submit = (timeRay) => { appendFilter(new FilterInsideTimeRay(timeRay)) } diff --git a/nest_frontend/components/interactive/BoxFilterHasPlace.js b/nest_frontend/components/interactive/BoxFilterHasPlace.js index 49c4d7a..b2ddba3 100644 --- a/nest_frontend/components/interactive/BoxFilterHasPlace.js +++ b/nest_frontend/components/interactive/BoxFilterHasPlace.js @@ -4,7 +4,7 @@ import FormInline from "../base/FormInline" import useRepositoryViewer from "../../hooks/useRepositoryViewer" import useStrings from "../../hooks/useStrings" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" -import { faLocationArrow, faPlus } from "@fortawesome/free-solid-svg-icons" +import { faLocationArrow, faMapMarkerAlt, faPlus } from "@fortawesome/free-solid-svg-icons" import { FilterWithPlace } from "../../objects/Filter" import ButtonIconOnly from "../base/ButtonIconOnly" @@ -33,7 +33,7 @@ export default function BoxFilterHasPlace({ ...props }) { {strings.searchBy}   - +   {strings.byHasPlace} diff --git a/nest_frontend/components/interactive/BoxFilterHashtag.js b/nest_frontend/components/interactive/BoxFilterHashtag.js index b1f5145..8f3ac34 100644 --- a/nest_frontend/components/interactive/BoxFilterHashtag.js +++ b/nest_frontend/components/interactive/BoxFilterHashtag.js @@ -1,6 +1,6 @@ import React from "react" import BoxFull from "../base/BoxFull" -import { faClock } from "@fortawesome/free-solid-svg-icons" +import { faClock, faHashtag } from "@fortawesome/free-solid-svg-icons" import useRepositoryViewer from "../../hooks/useRepositoryViewer" import useStrings from "../../hooks/useStrings" import { FilterHashtag } from "../../objects/Filter" @@ -30,9 +30,9 @@ export default function BoxFilterHashtag({ ...props }) { {strings.searchBy}   - +   - {strings.byTimePeriod} + {strings.byHashtag} } {...props} diff --git a/nest_frontend/components/interactive/BoxFilterLocation.js b/nest_frontend/components/interactive/BoxFilterLocation.js index c7d7114..7342478 100644 --- a/nest_frontend/components/interactive/BoxFilterLocation.js +++ b/nest_frontend/components/interactive/BoxFilterLocation.js @@ -3,7 +3,7 @@ import BoxFull from "../base/BoxFull" import useRepositoryViewer from "../../hooks/useRepositoryViewer" import useStrings from "../../hooks/useStrings" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" -import { faMapPin } from "@fortawesome/free-solid-svg-icons" +import { faLocationArrow, faMapPin } from "@fortawesome/free-solid-svg-icons" import FormInlineLocation from "./FormInlineLocation" import { FilterInsideMapArea } from "../../objects/Filter" @@ -13,6 +13,7 @@ import { FilterInsideMapArea } from "../../objects/Filter" * * It connects to the `mapViewHook` of the RepositoryViewer. * + * @deprecated to be refactored * @param props - Additional props to pass to the box. * @returns {JSX.Element} * @constructor @@ -23,7 +24,7 @@ export default function BoxFilterLocation({ ...props }) { const { appendFilter, mapViewHook } = useRepositoryViewer() const submit = () => { - appendFilter(new FilterInsideMapArea(false, mapViewHook.center, mapViewHook.radius)) + appendFilter(new FilterInsideMapArea(mapViewHook.mapArea)) } return ( @@ -32,7 +33,7 @@ export default function BoxFilterLocation({ ...props }) { {strings.searchBy}   - +   {strings.byZone} diff --git a/nest_frontend/components/interactive/BoxVisualizationMap.js b/nest_frontend/components/interactive/BoxVisualizationMap.js index 910ff21..3eea7b6 100644 --- a/nest_frontend/components/interactive/BoxVisualizationMap.js +++ b/nest_frontend/components/interactive/BoxVisualizationMap.js @@ -2,7 +2,7 @@ import React, { useContext, useMemo } from "react" import BoxMap from "../base/BoxMap" import ContextLanguage from "../../contexts/ContextLanguage" import { Marker, Popup } from "react-leaflet" -import { Location } from "../../objects/location" +import Coordinates from "../../objects/Coordinates" import ContextRepositoryViewer from "../../contexts/ContextRepositoryViewer" @@ -20,10 +20,12 @@ export default function BoxVisualizationMap({ ...props }) { const markers = useMemo( () => { return tweets.filter(tweet => tweet.location).map(tweet => { - const location = Location.fromTweet(tweet) + if(!tweet.location) return null + + const coords = Coordinates.fromCrawlerString(tweet.location) return ( - +

{tweet["content"]} diff --git a/nest_frontend/components/interactive/FormInlineHashtag.js b/nest_frontend/components/interactive/FormInlineHashtag.js index e6753b6..49cedf7 100644 --- a/nest_frontend/components/interactive/FormInlineHashtag.js +++ b/nest_frontend/components/interactive/FormInlineHashtag.js @@ -11,12 +11,11 @@ const INVALID_CHARACTERS = /([^a-z0-9_\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u00ff\u0 /** * A {@link FormInline} allowing the user to select a Twitter hashtag. * - * @param submit - Function called when the submit button is pressed. * @param props - Additional props to pass to the form. * @returns {JSX.Element} * @constructor */ -export default function FormInlineHashtag({ submit, ...props }) { +export default function FormInlineHashtag({ ...props }) { const validate = value => { return value.replace(INVALID_CHARACTERS, "") @@ -27,7 +26,6 @@ export default function FormInlineHashtag({ submit, ...props }) { textIcon={faHashtag} placeholder={"hashtag"} validate={validate} - submit={submit} {...props} /> ) diff --git a/nest_frontend/components/interactive/FormInlineLocation.js b/nest_frontend/components/interactive/FormInlineLocation.js index f55a6ca..ec07a41 100644 --- a/nest_frontend/components/interactive/FormInlineLocation.js +++ b/nest_frontend/components/interactive/FormInlineLocation.js @@ -47,21 +47,21 @@ export default function FormInlineLocation( className={Style.Radius} type={"text"} icon={radIcon} - value={`${mapViewHook.radius} m`} + value={`${Math.round(mapViewHook.mapArea.radius / 1000)} km`} disabled={true} /> { event.preventDefault() + if(!value) return submit(value) setValue("") } @@ -56,6 +57,7 @@ export default function FormInlineText( icon={buttonIcon} color={buttonColor} onClick={_onSubmit} + disabled={!value} /> ) diff --git a/nest_frontend/components/interactive/FormInlineTimeRay.js b/nest_frontend/components/interactive/FormInlineTimeRay.js index 90b5815..dd7b7ac 100644 --- a/nest_frontend/components/interactive/FormInlineTimeRay.js +++ b/nest_frontend/components/interactive/FormInlineTimeRay.js @@ -8,7 +8,7 @@ import ButtonToggleBeforeAfter from "./ButtonToggleBeforeAfter" import TimeRay from "../../objects/TimeRay" -const INVALID_CHARACTERS = /[^0-9TZ:+-]/g +const INVALID_CHARACTERS = /[^0-9TZ:+.-]/g /** @@ -40,12 +40,14 @@ export default function FormInlineTimeRay( const _onSubmit = event => { event.preventDefault() + if(!value) return + console.debug(value) submit(new TimeRay(isBefore, new Date(value))) setValue("") } const _onChange = event => { - setValue(validate(event.target.value.replace(INVALID_CHARACTERS, ""))) + setValue(validate(event.target.value.toUpperCase().replace(INVALID_CHARACTERS, ""))) } return ( @@ -67,6 +69,7 @@ export default function FormInlineTimeRay( icon={buttonIcon} color={buttonColor} onClick={_onSubmit} + disabled={!value} /> ) diff --git a/nest_frontend/components/interactive/FormInlineUser.js b/nest_frontend/components/interactive/FormInlineUser.js index 275e655..42ec2a8 100644 --- a/nest_frontend/components/interactive/FormInlineUser.js +++ b/nest_frontend/components/interactive/FormInlineUser.js @@ -9,12 +9,11 @@ const INVALID_CHARACTERS = /[^a-zA-Z0-9]/g /** * A {@link FormInline} allowing the user to select a Twitter user. * - * @param submit - Function called when the submit button is pressed. * @param props - Additional props to pass to the form. * @returns {JSX.Element} * @constructor */ -export default function FormInlineUser({ submit, ...props }) { +export default function FormInlineUser({ ...props }) { const validate = value => { return value.replace(INVALID_CHARACTERS, "") @@ -25,7 +24,6 @@ export default function FormInlineUser({ submit, ...props }) { textIcon={faAt} placeholder={"jack"} validate={validate} - submit={submit} {...props} /> ) diff --git a/nest_frontend/components/interactive/PickerFilter.js b/nest_frontend/components/interactive/PickerFilter.js index 478fe43..807081c 100644 --- a/nest_frontend/components/interactive/PickerFilter.js +++ b/nest_frontend/components/interactive/PickerFilter.js @@ -1,6 +1,14 @@ import React, { useContext } from "react" import ButtonIconOnly from "../base/ButtonIconOnly" -import { faAt, faClock, faFont, faHashtag, faLocationArrow, faMapPin } from "@fortawesome/free-solid-svg-icons" +import { + faAt, + faClock, + faFont, + faHashtag, + faLocationArrow, + faMapMarkerAlt, + faMapPin, +} from "@fortawesome/free-solid-svg-icons" import ButtonPicker from "./ButtonPicker" import ContextRepositoryViewer from "../../contexts/ContextRepositoryViewer" @@ -45,7 +53,7 @@ export default function PickerFilter({ ...props }) { currentTab={filterTab} setTab={setFilterTab} name={"place"} - icon={faLocationArrow} + icon={faMapMarkerAlt} /> { @@ -54,7 +62,7 @@ export default function PickerFilter({ ...props }) { }} disabled={filterTab === "location"} color={"Grey"} - icon={faMapPin} + icon={faLocationArrow} />

) diff --git a/nest_frontend/objects/Coordinates.js b/nest_frontend/objects/Coordinates.js index 10413f2..a27f965 100644 --- a/nest_frontend/objects/Coordinates.js +++ b/nest_frontend/objects/Coordinates.js @@ -1,6 +1,9 @@ /** * A pair of coordinates, latitude `lat` and longitude `lng`. */ +import { LatLng } from "leaflet/dist/leaflet-src.esm" + + export default class Coordinates { lat lng @@ -25,7 +28,7 @@ export default class Coordinates { if(!match) { throw new Error(`Invalid location string: ${str}`) } - return new Coordinates(match[0], match[1]) + return new Coordinates(match[1], match[2]) } /** @@ -62,4 +65,13 @@ export default class Coordinates { toArray() { return [this.lat, this.lng] } + + /** + * Transform this object in a {@link LatLng} / Leaflet compatible-one. + * + * @returns {LatLng} + */ + toLatLng() { + return new LatLng(this.lat, this.lng) + } } diff --git a/nest_frontend/objects/Filter.js b/nest_frontend/objects/Filter.js index c8f8892..a18584c 100644 --- a/nest_frontend/objects/Filter.js +++ b/nest_frontend/objects/Filter.js @@ -59,9 +59,9 @@ export class Filter { export class FilterContains extends Filter { string - constructor(word, negate = false) { + constructor(string, negate = false) { super(negate) - this.string = word.toLowerCase().trim() + this.string = string.toLowerCase().trim() } check(tweet) { @@ -85,7 +85,7 @@ export class FilterHashtag extends FilterContains { hashtag constructor(hashtag, negate = false) { - super(negate, `#${hashtag}`) + super(`#${hashtag}`, negate) this.hashtag = hashtag }