From 6870de4c7b173f6575f0e8ab79ad6718c38e1aae Mon Sep 17 00:00:00 2001
From: vjrj
Date: Thu, 8 Feb 2018 00:00:53 +0100
Subject: [PATCH 001/330] Style and text minor changes in home
---
imports/ui/pages/Index/Index-custom.scss | 22 ++++++++++++++++++----
imports/ui/pages/Index/Index.js | 6 ++++++
private/pages/credits-es.md | 4 ++--
public/locales/en/common.json | 6 +++++-
public/locales/es/common.json | 7 ++++++-
5 files changed, 37 insertions(+), 8 deletions(-)
diff --git a/imports/ui/pages/Index/Index-custom.scss b/imports/ui/pages/Index/Index-custom.scss
index a47e09b..7273007 100644
--- a/imports/ui/pages/Index/Index-custom.scss
+++ b/imports/ui/pages/Index/Index-custom.scss
@@ -57,7 +57,7 @@
@include breakpoint(mobile) {
.carousel-caption > h3 {
- font-size: 2.0rem;
+ font-size: 1.8rem;
}
}
@@ -72,7 +72,7 @@
color: #280B0B;
font-weight: bold;
text-transform: uppercase;
- font-size: 10px;
+ font-size: 20px;
}
p {
padding-top: 10px;
@@ -103,7 +103,7 @@
width: 30%;
left: 35%;
border: 14px solid #FFF;
- opacity: .8;
+ opacity: .6;
&:after {
content: "";
display: block;
@@ -125,7 +125,7 @@
/* position: absolute; */
font-size: 140px;
color: white;
- opacity: .8;
+ opacity: .6;
/* right: 35%; */
text-shadow: 2px 2px #280B0B;
}
@@ -287,12 +287,26 @@
text-align: left;
}
+.slides-footer {
+ position: relative;
+ bottom: 0px;
+ left: 0px;
+ text-align: left;
+}
+
+.slides-footer > p,
.crowd-footer > p {
font-size: 13px;
margin-left: 20px;
margin-right: 20px;
}
+.slides-footer > p > a,
+.slides-footer > p > a:hover {
+ color: #868e96 !important;
+ text-shadow: 2px 2px 1px #280B0B;
+}
+
#carouselSndIndicators > div > div.carousel-item.carousel-snd-item-1 > div > h3 > span > a {
color: white;
}
diff --git a/imports/ui/pages/Index/Index.js b/imports/ui/pages/Index/Index.js
index 04afd4f..270800c 100644
--- a/imports/ui/pages/Index/Index.js
+++ b/imports/ui/pages/Index/Index.js
@@ -197,12 +197,18 @@ class Index extends Component {
Ayúdanos a combatir el cambio climático y a proteger el medioambiente
+
diff --git a/private/pages/credits-es.md b/private/pages/credits-es.md
index a1202f2..b1ada88 100644
--- a/private/pages/credits-es.md
+++ b/private/pages/credits-es.md
@@ -8,8 +8,8 @@ Reconocemos el uso de datos e imágenes de LANCE FIRMS operadas por NASA/GSFC/Ea
* Diapositiva 2 del Inicio: Silverhorne, Estados Unidos, foto de Nathan Anderson [fuente](https://unsplash.com/photos/ZLOZC1uUdns).
* Diapositiva 3 del Inicio: Bitterroot National Forest, Montana, Estados Unidos, foto de John McColgan, Public Domain, [fuente](https://commons.wikimedia.org/wiki/File:Deerfire_high_res.jpg).
* Diapositiva 4 del Inicio: Cox Valley Fire, Olympic NP, Estados Unidos, agosto de 2016, [fuente](https://www.nps.gov/olym/learn/management/current-fire-status.htm).
-* Diapositiva secundaria 1 del Inicio: La Tuna fire and nighttime city lights, LA, Estados Unidos, septiembre de 2017, foto de Scott L, CC-BY-SA 2.0, [source](https://commons.wikimedia.org/wiki/File:La_Tuna_fire_and_cityscape_1.jpg)
-* Diapositiva secundaria 2 del Inicio: Smog over Almaty city, Kazakhstan, enero de 2014, foto de Igors Jefimovs, CC-BY 3.0, [source](https://commons.wikimedia.org/wiki/File:Smog_over_Almaty.jpg)
+* Diapositiva secundaria 1 del Inicio: Fuego en La Tuna y luces nocturnas de ciudad, LA, Estados Unidos, septiembre de 2017, foto de Scott L, CC-BY-SA 2.0, [source](https://commons.wikimedia.org/wiki/File:La_Tuna_fire_and_cityscape_1.jpg)
+* Diapositiva secundaria 2 del Inicio: Polución en la ciudad de Almaty, Kazakhstan, enero de 2014, foto de Igors Jefimovs, CC-BY 3.0, [source](https://commons.wikimedia.org/wiki/File:Smog_over_Almaty.jpg)
* Foto de inicio de fuego forestal: [Incendios forestales de California, 23 de octubre de 2007](https://commons.wikimedia.org/wiki/File:California_Wildfires_October_23_2007.jpg), [fuente](http://www.nasa.gov/vision/earth/lookingatearth/socal_wildfires_oct07.html).
* Avatar predeterminado: un Emoji coloreado del proyecto Noto, publicado bajo licencia Apache, [fuente](https://commons.wikimedia.org/wiki/File:Emoji_u1f469_1f3fd_200d_1f692.svg).
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index df512c3..09ff3b9 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -165,5 +165,9 @@
"Información adicional sobre fuego": "Additional information about fire",
"CO2emisions":
"Did you know that wildfires <1>produce as much CO² as cars1> and <3>about ⅕ of all our carbon emissions3>?",
- "Ayúdanos a combatir el cambio climático y a proteger el medioambiente": "Help us fight climate change and protect the environment"
+ "Ayúdanos a combatir el cambio climático y a proteger el medioambiente": "Help us fight climate change and protect the environment",
+ "Fuego en La Tuna, Los Ángeles, Estados Unidos, septiembre de 2017":
+ "La Tuna fire, LA, United States, September 2017",
+ "Polución en la ciudad de Almaty, Kazakhstan, enero de 2014":
+ "Smog over Almaty city, Kazakhstan, January 2014"
}
diff --git a/public/locales/es/common.json b/public/locales/es/common.json
index ffefe05..e90c4a0 100644
--- a/public/locales/es/common.json
+++ b/public/locales/es/common.json
@@ -256,5 +256,10 @@
"Información adicional sobre fuego",
"CO2emisions":
"¿Sabías que los fuegos <1>producen tanto CO² como los coches1> y <3>aproximadamente ⅕ de todas nuestras emisiones3>?",
- "Ayúdanos a combatir el cambio climático": "Ayúdanos a combatir el cambio climático"
+ "Ayúdanos a combatir el cambio climático y a proteger el medioambiente":
+ "Ayúdanos a combatir el cambio climático y a proteger el medioambiente",
+ "Fuego en La Tuna, Los Ángeles, Estados Unidos, septiembre de 2017":
+ "Fuego en La Tuna, Los Ángeles, Estados Unidos, septiembre de 2017",
+ "Polución en la ciudad de Almaty, Kazakhstan, enero de 2014":
+ "Polución en la ciudad de Almaty, Kazakhstan, enero de 2014"
}
From eca58df6c2de24d9bc6f0e49ddbbe1faadd7939e Mon Sep 17 00:00:00 2001
From: vjrj
Date: Thu, 8 Feb 2018 11:15:23 +0100
Subject: [PATCH 002/330] Fire popupsand active/archive urls
---
.../api/ActiveFires/server/publications.js | 1 +
imports/api/Fires/server/publications.js | 99 ++++++++++++++-----
imports/startup/client/i18n.js | 2 +-
imports/startup/server/i18n.js | 2 +-
imports/ui/components/Maps/FireCircleMark.js | 19 +++-
imports/ui/components/Maps/FireIconMark.js | 24 ++++-
imports/ui/components/Maps/FireList.js | 10 +-
imports/ui/components/Maps/FirePopup.js | 43 ++++++++
imports/ui/layouts/App/App.js | 1 +
imports/ui/pages/Fires/Fires.js | 31 +++++-
imports/ui/pages/FiresMap/FiresMap.js | 5 +
imports/ui/pages/Index/Index.js | 6 +-
imports/ui/stylesheets/custom.scss | 12 ++-
public/locales/en/common.json | 8 +-
public/locales/es/common.json | 8 +-
15 files changed, 222 insertions(+), 49 deletions(-)
create mode 100644 imports/ui/components/Maps/FirePopup.js
diff --git a/imports/api/ActiveFires/server/publications.js b/imports/api/ActiveFires/server/publications.js
index 0b9b2d7..278d937 100644
--- a/imports/api/ActiveFires/server/publications.js
+++ b/imports/api/ActiveFires/server/publications.js
@@ -27,6 +27,7 @@ const activefires = (northEastLng, northEastLat, southWestLng, southWestLat) =>
fields: {
lat: 1,
lon: 1,
+ when: 1,
scan: 1
}
});
diff --git a/imports/api/Fires/server/publications.js b/imports/api/Fires/server/publications.js
index 68eb4c8..575c7af 100644
--- a/imports/api/Fires/server/publications.js
+++ b/imports/api/Fires/server/publications.js
@@ -7,6 +7,7 @@ import urlEnc from '/imports/modules/url-encode';
import { Promise } from 'meteor/promise';
import NodeGeocoder from 'node-geocoder';
import { gmapServerKey } from '/imports/startup/server/IPGeocoder';
+import ActiveFiresCollection from '/imports/api/ActiveFires/ActiveFires';
import FiresCollection from '../Fires';
function findFire(unsealed) {
@@ -36,6 +37,75 @@ const unseal = async (obj) => {
const unsealW = obj => unseal(obj);
+const fixConfidence = (obj) => {
+ if (typeof obj.confidence === 'string') {
+ obj.confidence = Number.parseInt(obj.confidence, 10);
+ }
+ if (typeof obj.confidence === 'undefined' || isNaN(obj.confidence)) {
+ delete obj.confidence;
+ }
+ return obj;
+};
+
+const findOrCreateFire = (obj) => {
+ const fire = findFire(obj);
+ // console.log(`Found: ${fire.count()}`);
+ if (!obj.address) {
+ try {
+ const rev = Promise.await(geocoder.reverse({ lat: obj.lat, lon: obj.lon }));
+ if (rev[0]) {
+ obj.address = rev[0].formattedAddress;
+ }
+ // console.log(obj.address);
+ } catch (reve) {
+ console.warn(reve);
+ }
+ }
+ if (fire.count() === 0) {
+ // const result =
+ // console.log('Creating new fire');
+ FiresCollection.upsert({ ourid: obj.ourid, when: obj.when, type: obj.type }, { $set: obj }, { multi: false, upsert: true });
+ // console.log(JSON.stringify(result));
+ }
+ return findFire(obj);
+};
+
+Meteor.publish('fireFromActiveId', function fireFromActiveId(_id) {
+ try {
+ check(_id, String);
+ // console.log(`Looking for active fire ${_id}`);
+ const fire = ActiveFiresCollection.findOne(new Meteor.Collection.ObjectID(_id));
+ if (fire) {
+ // console.info(`Active fire found: ${_id}`);
+ return findOrCreateFire(fixConfidence(fire));
+ }
+ console.info(`Active fire not found: ${_id}`);
+ // Not found in active fires!
+ return this.ready();
+ } catch (e) {
+ console.info(`Active fire not found (with error): ${_id}`);
+ return this.ready();
+ }
+});
+
+Meteor.publish('fireFromId', function fireFromId(_id) {
+ try {
+ check(_id, String);
+ // console.log(`Looking for archive fire ${_id}`);
+ const fire = FiresCollection.find(new Meteor.Collection.ObjectID(_id));
+ if (fire.count() !== 0) {
+ // console.info(`Archive fire found: ${_id}`);
+ return fire;
+ }
+ console.info(`Fire not found: ${_id}`);
+ // Not found in active fires!
+ return this.ready();
+ } catch (e) {
+ console.info(`Active fire not found (with error): ${_id}`);
+ return this.ready();
+ }
+});
+
Meteor.publish('fireFromHash', function fireFromHash(fireEnc) {
check(fireEnc, String);
try {
@@ -55,32 +125,9 @@ Meteor.publish('fireFromHash', function fireFromHash(fireEnc) {
unsealed.updatedAt = !u ? new Date() : new Date(u);
// console.log(unsealed);
// FIXME:
- if (typeof unsealed.confidence === 'string') {
- unsealed.confidence = Number.parseInt(unsealed.confidence, 10);
- }
- if (typeof unsealed.confidence === 'undefined' || isNaN(unsealed.confidence)) {
- delete unsealed.confidence;
- }
- FiresCollection.schema.validate(unsealed);
- const fire = findFire(unsealed);
- // console.log(`Found: ${fire.count()}`);
- if (!unsealed.address) {
- try {
- const rev = Promise.await(geocoder.reverse({ lat: unsealed.lat, lon: unsealed.lon }));
- if (rev[0]) {
- unsealed.address = rev[0].formattedAddress;
- }
- // console.log(unsealed.address);
- } catch (reve) {
- console.warn(reve);
- }
- }
- if (fire.count() === 0) {
- // const result =
- FiresCollection.upsert({ ourid: unsealed.ourid, when: unsealed.when, type: unsealed.type }, { $set: unsealed }, { multi: false, upsert: true });
- // console.log(JSON.stringify(result));
- }
- return findFire(unsealed);
+ const unsealedFix = fixConfidence(unsealed);
+ FiresCollection.schema.validate(unsealedFix);
+ return findOrCreateFire(unsealedFix);
/* console.log(`fires: ${fire.count()}`);
* return fire; */
} catch (e) {
diff --git a/imports/startup/client/i18n.js b/imports/startup/client/i18n.js
index 2a3398d..d4e3fcc 100644
--- a/imports/startup/client/i18n.js
+++ b/imports/startup/client/i18n.js
@@ -53,7 +53,7 @@ i18nOpts.react = {
nsMode: 'default' */
};
-const sendMissing = true;
+const sendMissing = Meteor.isDevelopment;
if (sendMissing && !Meteor.isProduction) {
i18nOpts.sendMissing = true;
i18nOpts.sendMissingTo = 'fallback';
diff --git a/imports/startup/server/i18n.js b/imports/startup/server/i18n.js
index e6f3172..053d487 100644
--- a/imports/startup/server/i18n.js
+++ b/imports/startup/server/i18n.js
@@ -11,7 +11,7 @@ i18nOpts.backend.addPath = `${Meteor.rootPath}/../web.browser/app/${i18nOpts.bac
// console.log(i18nOpts.backend.loadPath);
i18nOpts.debug = false;
-i18nOpts.saveMissing = true;
+i18nOpts.saveMissing = Meteor.isDevelopment;
i18nOpts.initImmediate = false;
i18n
diff --git a/imports/ui/components/Maps/FireCircleMark.js b/imports/ui/components/Maps/FireCircleMark.js
index 477fba7..d90e312 100644
--- a/imports/ui/components/Maps/FireCircleMark.js
+++ b/imports/ui/components/Maps/FireCircleMark.js
@@ -1,19 +1,32 @@
import React from 'react';
import { Circle } from 'react-leaflet';
import PropTypes from 'prop-types';
+import FirePopup from './FirePopup';
const FireCircleMark = ({
lat,
lon,
- scan
+ nasa,
+ scan,
+ id,
+ when,
+ history,
+ t
}) => (
-
+
+
+
);
FireCircleMark.propTypes = {
scan: PropTypes.number.isRequired,
lat: PropTypes.number.isRequired,
- lon: PropTypes.number.isRequired
+ lon: PropTypes.number.isRequired,
+ nasa: PropTypes.bool.isRequired,
+ id: PropTypes.object.isRequired,
+ history: PropTypes.object.isRequired,
+ when: PropTypes.instanceOf(Date).isRequired,
+ t: PropTypes.func.isRequired
};
export default FireCircleMark;
diff --git a/imports/ui/components/Maps/FireIconMark.js b/imports/ui/components/Maps/FireIconMark.js
index 65f588d..f83b4bf 100644
--- a/imports/ui/components/Maps/FireIconMark.js
+++ b/imports/ui/components/Maps/FireIconMark.js
@@ -3,15 +3,25 @@ import React from 'react';
import { CircleMarker, Marker } from 'react-leaflet';
import PropTypes from 'prop-types';
import { fireIcon, nFireIcon } from '/imports/ui/components/Maps/Icons';
+import { translate } from 'react-i18next';
+import FirePopup from './FirePopup';
const FireIconMark = ({
lat,
lon,
- nasa
+ nasa,
+ id,
+ history,
+ when,
+ t
}) => (
-
-
+
+
+
+
+
+
);
@@ -19,7 +29,11 @@ FireIconMark.propTypes = {
// https://github.com/PaulLeCam/react-leaflet/tree/master/src/propTypes
lat: PropTypes.number.isRequired,
lon: PropTypes.number.isRequired,
- nasa: PropTypes.bool.isRequired
+ nasa: PropTypes.bool.isRequired,
+ id: PropTypes.object.isRequired,
+ history: PropTypes.object.isRequired,
+ when: PropTypes.instanceOf(Date).isRequired,
+ t: PropTypes.func.isRequired
};
-export default FireIconMark;
+export default translate([], { wait: true })(FireIconMark);
diff --git a/imports/ui/components/Maps/FireList.js b/imports/ui/components/Maps/FireList.js
index 60d8b3e..be20855 100644
--- a/imports/ui/components/Maps/FireList.js
+++ b/imports/ui/components/Maps/FireList.js
@@ -9,18 +9,18 @@ import FirePixel from '/imports/ui/components/Maps/FirePixel';
export default function FireList(props) {
const {
- fires, scale, useMarkers, nasa
+ fires, scale, useMarkers, nasa, t, history
} = props;
const useMarks = useMarkers && scale;
const usePixel = !nasa || !scale;
/* console.log(`Using marks: ${useMarks}, using pixels: ${usePixel}`); */
let items;
if (useMarks) {
- items = fires.map(({ _id, ...otherProps }) => ( ));
+ items = fires.map(({ _id, ...otherProps }) => ( ));
} else if (usePixel) {
items = fires.map(({ _id, ...otherProps }) => ( ));
} else {
- items = fires.map(({ _id, ...otherProps }) => ( ));
+ items = fires.map(({ _id, ...otherProps }) => ( ));
}
return ({items}
);
}
@@ -29,5 +29,7 @@ FireList.propTypes = {
fires: PropTypes.array.isRequired,
scale: PropTypes.bool.isRequired,
useMarkers: PropTypes.bool.isRequired,
- nasa: PropTypes.bool.isRequired
+ nasa: PropTypes.bool.isRequired,
+ history: PropTypes.object.isRequired,
+ t: PropTypes.func.isRequired
};
diff --git a/imports/ui/components/Maps/FirePopup.js b/imports/ui/components/Maps/FirePopup.js
new file mode 100644
index 0000000..c3f2f52
--- /dev/null
+++ b/imports/ui/components/Maps/FirePopup.js
@@ -0,0 +1,43 @@
+/* eslint-disable import/no-absolute-path */
+import React, { Fragment } from 'react';
+import { Popup, Tooltip } from 'react-leaflet';
+import PropTypes from 'prop-types';
+import { translate } from 'react-i18next';
+import moment from 'moment';
+
+const FirePopup = ({
+ lat,
+ lon,
+ nasa,
+ id,
+ history,
+ when,
+ t
+}) => (
+
+
+
+ {t('Coordenadas:')} {lat}, {lon}
+ {t('Fuente')}: {t(nasa ? 'NASA' : 'nuestros usuarios/as')}
+ {t('Detectado')}: {moment(when).fromNow()}
+
+ history.push(`/fire/active/${id}`)}>{t('Más información sobre este fuego')}
+
+
+
+ {t('Pulsa para más información')}
+
+);
+
+FirePopup.propTypes = {
+ // https://github.com/PaulLeCam/react-leaflet/tree/master/src/propTypes
+ lat: PropTypes.number.isRequired,
+ lon: PropTypes.number.isRequired,
+ nasa: PropTypes.bool.isRequired,
+ id: PropTypes.object.isRequired,
+ history: PropTypes.object.isRequired,
+ when: PropTypes.instanceOf(Date).isRequired,
+ t: PropTypes.func.isRequired
+};
+
+export default translate([], { wait: true })(FirePopup);
diff --git a/imports/ui/layouts/App/App.js b/imports/ui/layouts/App/App.js
index 4b1eb86..4e3ff27 100644
--- a/imports/ui/layouts/App/App.js
+++ b/imports/ui/layouts/App/App.js
@@ -83,6 +83,7 @@ const App = props => (
+
diff --git a/imports/ui/pages/Fires/Fires.js b/imports/ui/pages/Fires/Fires.js
index 3196865..52d1b81 100644
--- a/imports/ui/pages/Fires/Fires.js
+++ b/imports/ui/pages/Fires/Fires.js
@@ -34,6 +34,10 @@ class Fire extends React.Component {
componentWillReceiveProps(nextProps) {
if (this.props.when !== nextProps.when || this.props.loading !== nextProps.loading || this.props.notfound !== nextProps.notfound) {
// console.log(`Next when ${nextProps.when}`);
+ if (nextProps.fire && (nextProps.active || nextProps.fromHash)) {
+ // change url to archive with new _id
+ nextProps.history.replace(`/fire/archive/${nextProps.fire._id}`);
+ }
this.setState({
loading: nextProps.loading,
notfound: nextProps.notfound,
@@ -124,7 +128,7 @@ class Fire extends React.Component {
{Object.keys(FalsePositiveTypes).map(key => (
this.onTypeSelect(key)}
+ onClick={() => this.onTypeSelect(key)}
key={key}
type="button"
>
@@ -161,8 +165,11 @@ class Fire extends React.Component {
Fire.propTypes = {
t: PropTypes.func.isRequired,
+ history: PropTypes.object.isRequired,
loading: PropTypes.bool.isRequired,
notfound: PropTypes.bool.isRequired,
+ fromHash: PropTypes.bool.isRequired,
+ active: PropTypes.bool.isRequired,
when: PropTypes.instanceOf(Date),
fire: PropTypes.object
};
@@ -173,8 +180,24 @@ Fire.defaultProps = {
// export default translate([], { wait: true })(withTracker((props) => {
const FireContainer = withTracker(({ match }) => {
- const fireEncrypt = match.params.id;
- const subscription = Meteor.subscribe('fireFromHash', fireEncrypt);
+ const id = match.params.id;
+ const fireType = match.params.type;
+ let subscription;
+ const active = fireType === 'active';
+ const archive = fireType === 'archive';
+ let fromHash = false;
+
+ if (active) {
+ subscription = Meteor.subscribe('fireFromActiveId', id);
+ } else if (archive) {
+ subscription = Meteor.subscribe('fireFromId', id);
+ } else {
+ console.log('Seems a fire from enc hash');
+ fromHash = true;
+ subscription = Meteor.subscribe('fireFromHash', id);
+ }
+
+ // console.log(`Type of '${fireType}' fire, active: ${active}, archive: ${archive}, fromHash: ${fromHash}`);
// console.log(`Subs ready: ${subscription.ready()}, fire: ${JSON.stringify(FiresCollection.findOne())}`);
const loading = !subscription.ready();
const notfound = !loading && FiresCollection.find().count() === 0;
@@ -182,6 +205,8 @@ const FireContainer = withTracker(({ match }) => {
* console.log(`Not found fire: ${notfound}`); */
return {
loading,
+ active,
+ fromHash,
fire: FiresCollection.findOne(),
notfound,
when: subscription.ready() && FiresCollection.findOne() ? FiresCollection.findOne().when : null
diff --git a/imports/ui/pages/FiresMap/FiresMap.js b/imports/ui/pages/FiresMap/FiresMap.js
index 62d61b8..91370e6 100644
--- a/imports/ui/pages/FiresMap/FiresMap.js
+++ b/imports/ui/pages/FiresMap/FiresMap.js
@@ -211,12 +211,16 @@ class FiresMap extends React.Component {
{!this.props.loading &&
= MAXZOOM}
useMarkers={this.state.useMarkers}
nasa
/>
-
+
);
}
@@ -233,7 +233,7 @@ class Index extends Component {
- {this.getMap(isAnyMobile)}
+ {this.getMap(isAnyMobile, this.props)}
diff --git a/imports/ui/stylesheets/custom.scss b/imports/ui/stylesheets/custom.scss
index 87e9027..8bd13a4 100644
--- a/imports/ui/stylesheets/custom.scss
+++ b/imports/ui/stylesheets/custom.scss
@@ -39,4 +39,14 @@ h4.page-header {
#react-root > div > div.container > div > div.alert {
margin-top: 20px;
-}
\ No newline at end of file
+}
+
+div.leaflet-pane.leaflet-popup-pane > div > div.leaflet-popup-content-wrapper > div > p,
+div.leaflet-pane.leaflet-popup-pane > div > div.leaflet-popup-content-wrapper > div > p > a {
+ font-size: 12px;
+}
+
+div.leaflet-pane.leaflet-popup-pane > div > div.leaflet-popup-content-wrapper > div > p > a:hover,
+div.leaflet-pane.leaflet-popup-pane > div > div.leaflet-popup-content-wrapper > div > p > a {
+// color: $todos-palette2;
+}
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index 09ff3b9..00a2224 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -169,5 +169,11 @@
"Fuego en La Tuna, Los Ángeles, Estados Unidos, septiembre de 2017":
"La Tuna fire, LA, United States, September 2017",
"Polución en la ciudad de Almaty, Kazakhstan, enero de 2014":
- "Smog over Almaty city, Kazakhstan, January 2014"
+ "Smog over Almaty city, Kazakhstan, January 2014",
+ "Fuente": "Source",
+ "NASA": "NASA",
+ "nuestros usuarios/as": "our users",
+ "Pulsa para más información": "Click for more information",
+ "Detectado":
+ "Detected"
}
diff --git a/public/locales/es/common.json b/public/locales/es/common.json
index e90c4a0..db61fc1 100644
--- a/public/locales/es/common.json
+++ b/public/locales/es/common.json
@@ -261,5 +261,11 @@
"Fuego en La Tuna, Los Ángeles, Estados Unidos, septiembre de 2017":
"Fuego en La Tuna, Los Ángeles, Estados Unidos, septiembre de 2017",
"Polución en la ciudad de Almaty, Kazakhstan, enero de 2014":
- "Polución en la ciudad de Almaty, Kazakhstan, enero de 2014"
+ "Polución en la ciudad de Almaty, Kazakhstan, enero de 2014",
+ "Fuente": "Fuente",
+ "NASA": "NASA",
+ "nuestros usuarios/as": "nuestros usuarios/as",
+ "Pulsa para más información": "Pulsa para más información",
+ "Detectado":
+ "Detectado"
}
From 735dd555316a16dfd5e12f76e30efe892826490d Mon Sep 17 00:00:00 2001
From: vjrj
Date: Thu, 8 Feb 2018 15:54:47 +0100
Subject: [PATCH 003/330] Fire industries marks
---
.../api/FalsePositives/server/publications.js | 59 ++++++++++++++++++
imports/startup/server/api.js | 1 +
imports/ui/components/Maps/FireIconMark.js | 27 +++++---
imports/ui/components/Maps/FireList.js | 9 +--
imports/ui/components/Maps/FirePopup.js | 11 ++--
imports/ui/components/Maps/Icons.js | 6 ++
imports/ui/pages/FiresMap/FiresMap.js | 30 ++++++++-
private/pages/credits-en.md | 1 +
private/pages/credits-es.md | 1 +
public/industry-marker.png | Bin 0 -> 339 bytes
10 files changed, 126 insertions(+), 19 deletions(-)
create mode 100644 imports/api/FalsePositives/server/publications.js
create mode 100644 public/industry-marker.png
diff --git a/imports/api/FalsePositives/server/publications.js b/imports/api/FalsePositives/server/publications.js
new file mode 100644
index 0000000..9585907
--- /dev/null
+++ b/imports/api/FalsePositives/server/publications.js
@@ -0,0 +1,59 @@
+/* global Counter */
+/* eslint-disable import/no-absolute-path */
+/* eslint-disable prefer-arrow-callback */
+
+import { Meteor } from 'meteor/meteor';
+import { check } from 'meteor/check';
+import { NumberBetween } from '/imports/modules/server/other-checks';
+import FalsePositives from '../FalsePositives';
+
+const counter = new Counter('countFalsePositives', FalsePositives.find({}));
+
+Meteor.publish('falsePositivesTotal', function total() {
+ return counter;
+});
+
+const falsePositives = (northEastLng, northEastLat, southWestLng, southWestLat) => {
+ const fires = FalsePositives.find({
+ geo: {
+ $geoWithin: {
+ $box: [
+ [southWestLng, southWestLat],
+ [northEastLng, northEastLat]
+ ]
+ }
+ }
+ }, {
+ fields: {
+ geo: 1,
+ // type: 1,
+ // when: 1,
+ fireId: 1
+ }
+ }).serverTransform(function transformDoc(odoc) {
+ const doc = odoc;
+ // Destructuring gives me an error: "Cannot destructure property `geo` of 'undefined'"
+ const geo = doc.geo;
+ if (geo) {
+ doc.lat = geo.coordinates[1];
+ doc.lon = geo.coordinates[0];
+ }
+ doc._id = doc.fireId;
+ doc.id = doc.fireId;
+ delete doc.geo;
+ // console.log(doc);
+ return doc;
+ });
+ // console.log(`Fires total: ${fires.count()}`);
+ return fires;
+};
+
+Meteor.publishTransformed('falsePositivesMyloc', function falsePositivesInMyLoc(northEastLng, northEastLat, southWestLng, southWestLat) {
+ // latitude -90 and 90 and the longitude between -180 and 180
+ check(northEastLng, NumberBetween(-180, 180));
+ check(southWestLat, NumberBetween(-90, 90));
+ check(southWestLng, NumberBetween(-180, 180));
+ check(northEastLat, NumberBetween(-90, 90));
+
+ return falsePositives(northEastLng, northEastLat, southWestLng, southWestLat);
+});
diff --git a/imports/startup/server/api.js b/imports/startup/server/api.js
index 0fcde5d..73f1fa2 100644
--- a/imports/startup/server/api.js
+++ b/imports/startup/server/api.js
@@ -26,3 +26,4 @@ import '../../api/SiteSettings/methods';
import '../../api/SiteSettings/server/publications';
import '../../api/FalsePositives/methods';
+import '../../api/FalsePositives/server/publications';
diff --git a/imports/ui/components/Maps/FireIconMark.js b/imports/ui/components/Maps/FireIconMark.js
index f83b4bf..5082edf 100644
--- a/imports/ui/components/Maps/FireIconMark.js
+++ b/imports/ui/components/Maps/FireIconMark.js
@@ -2,7 +2,7 @@
import React from 'react';
import { CircleMarker, Marker } from 'react-leaflet';
import PropTypes from 'prop-types';
-import { fireIcon, nFireIcon } from '/imports/ui/components/Maps/Icons';
+import { fireIcon, nFireIcon, industryIcon } from '/imports/ui/components/Maps/Icons';
import { translate } from 'react-i18next';
import FirePopup from './FirePopup';
@@ -12,16 +12,24 @@ const FireIconMark = ({
nasa,
id,
history,
+ falsePositives,
when,
t
}) => (
-
-
-
-
-
-
+ { !falsePositives &&
+
+
+ }
+ { falsePositives &&
+
+ { /* disabled because was a past fire (and can be marked multiple times) */ false && }
+
+ }
+ { !falsePositives &&
+
+
+ }
);
@@ -29,10 +37,11 @@ FireIconMark.propTypes = {
// https://github.com/PaulLeCam/react-leaflet/tree/master/src/propTypes
lat: PropTypes.number.isRequired,
lon: PropTypes.number.isRequired,
- nasa: PropTypes.bool.isRequired,
+ nasa: PropTypes.bool,
+ falsePositives: PropTypes.bool.isRequired,
id: PropTypes.object.isRequired,
history: PropTypes.object.isRequired,
- when: PropTypes.instanceOf(Date).isRequired,
+ when: PropTypes.instanceOf(Date),
t: PropTypes.func.isRequired
};
diff --git a/imports/ui/components/Maps/FireList.js b/imports/ui/components/Maps/FireList.js
index be20855..f4a851c 100644
--- a/imports/ui/components/Maps/FireList.js
+++ b/imports/ui/components/Maps/FireList.js
@@ -9,17 +9,17 @@ import FirePixel from '/imports/ui/components/Maps/FirePixel';
export default function FireList(props) {
const {
- fires, scale, useMarkers, nasa, t, history
+ fires, scale, useMarkers, nasa, t, history, falsePositives
} = props;
const useMarks = useMarkers && scale;
const usePixel = !nasa || !scale;
/* console.log(`Using marks: ${useMarks}, using pixels: ${usePixel}`); */
let items;
if (useMarks) {
- items = fires.map(({ _id, ...otherProps }) => ( ));
- } else if (usePixel) {
+ items = fires.map(({ _id, ...otherProps }) => ( ));
+ } else if (usePixel && !falsePositives) {
items = fires.map(({ _id, ...otherProps }) => ( ));
- } else {
+ } else if (!falsePositives) {
items = fires.map(({ _id, ...otherProps }) => ( ));
}
return ({items}
);
@@ -30,6 +30,7 @@ FireList.propTypes = {
scale: PropTypes.bool.isRequired,
useMarkers: PropTypes.bool.isRequired,
nasa: PropTypes.bool.isRequired,
+ falsePositives: PropTypes.bool.isRequired,
history: PropTypes.object.isRequired,
t: PropTypes.func.isRequired
};
diff --git a/imports/ui/components/Maps/FirePopup.js b/imports/ui/components/Maps/FirePopup.js
index c3f2f52..8e7d2a8 100644
--- a/imports/ui/components/Maps/FirePopup.js
+++ b/imports/ui/components/Maps/FirePopup.js
@@ -18,10 +18,11 @@ const FirePopup = ({
{t('Coordenadas:')} {lat}, {lon}
- {t('Fuente')}: {t(nasa ? 'NASA' : 'nuestros usuarios/as')}
- {t('Detectado')}: {moment(when).fromNow()}
+ {nasa && {t('Fuente')}: {t(nasa ? 'NASA' : 'nuestros usuarios/as')} }
+ {when && {t('Detectado')}: {moment(when).fromNow()} }
- history.push(`/fire/active/${id}`)}>{t('Más información sobre este fuego')}
+ { /* if nasa === null means that the is a false positive fire */ }
+ history.push(`/fire/${nasa ? 'active' : 'archive'}/${id}`)}>{t('Más información sobre este fuego')}
@@ -33,10 +34,10 @@ FirePopup.propTypes = {
// https://github.com/PaulLeCam/react-leaflet/tree/master/src/propTypes
lat: PropTypes.number.isRequired,
lon: PropTypes.number.isRequired,
- nasa: PropTypes.bool.isRequired,
+ nasa: PropTypes.bool,
id: PropTypes.object.isRequired,
history: PropTypes.object.isRequired,
- when: PropTypes.instanceOf(Date).isRequired,
+ when: PropTypes.instanceOf(Date),
t: PropTypes.func.isRequired
};
diff --git a/imports/ui/components/Maps/Icons.js b/imports/ui/components/Maps/Icons.js
index 7d8dc1f..2c04886 100644
--- a/imports/ui/components/Maps/Icons.js
+++ b/imports/ui/components/Maps/Icons.js
@@ -12,6 +12,12 @@ export const fireIcon = new Leaflet.Icon({
* popupAnchor: [-3, -76]// point from which the popup should open relative to the iconAnchor */
});
+export const industryIcon = new Leaflet.Icon({
+ iconUrl: '/industry-marker.png',
+ iconSize: [32, 37],
+ iconAnchor: [16, 20]
+});
+
export const nFireIcon = new Leaflet.Icon({
iconUrl: '/n-fire-marker.png',
iconSize: [16, 24], // size of the icon
diff --git a/imports/ui/pages/FiresMap/FiresMap.js b/imports/ui/pages/FiresMap/FiresMap.js
index 91370e6..802948d 100644
--- a/imports/ui/pages/FiresMap/FiresMap.js
+++ b/imports/ui/pages/FiresMap/FiresMap.js
@@ -26,6 +26,7 @@ import FromNow from '/imports/ui/components/FromNow/FromNow';
import Loading from '/imports/ui/components/Loading/Loading';
import ActiveFiresCollection from '/imports/api/ActiveFires/ActiveFires';
import FireAlertsCollection from '/imports/api/FireAlerts/FireAlerts';
+import FalsePositivesCollection from '/imports/api/FalsePositives/FalsePositives';
import SiteSettings from '/imports/api/SiteSettings/SiteSettings';
import UserSubsToFiresCollection from '/imports/api/Subscriptions/Subscriptions';
import { isNotHomeAndMobile, isChrome } from '/imports/ui/components/Utils/isMobile';
@@ -134,7 +135,10 @@ class FiresMap extends React.Component {
render() {
const { t } = this.props;
- console.log(`Rendering ${this.props.loading ? 'loading' : 'LOADED'} map ${this.props.activefires.length + this.props.firealerts.length} of ${this.props.activefirestotal} total. Subs users ready ${this.props.subsready} (${this.props.userSubs.length}), reactive ${this.state.viewport.zoom >= MAXZOOMREACTIVE}`);
+ console.log(`Rendering ${this.props.loading ? 'loading' : 'LOADED'} map ${this.props.activefires.length + this.props.firealerts.length} of ${this.props.activefirestotal} total. False positives: ${this.props.falsePositives.length}. Subs users ready ${this.props.subsready} (${this.props.userSubs.length}), reactive ${this.state.viewport.zoom >= MAXZOOMREACTIVE}`);
+ if (Meteor.isDevelopment) {
+ console.log(`False positives total: ${this.props.falsePositivesTotal}`);
+ }
return (
/* Large number of markers:
https://stackoverflow.com/questions/43015854/large-dataset-of-markers-or-dots-in-leaflet/43019740#43019740 */
@@ -210,6 +214,15 @@ class FiresMap extends React.Component {
{/* http://wiki.openstreetmap.org/wiki/Tile_servers */}
{!this.props.loading &&
+ = MAXZOOM}
+ useMarkers={this.state.useMarkers}
+ nasa={false}
+ falsePositives
+ />
= MAXZOOM}
useMarkers={this.state.useMarkers}
nasa
+ falsePositives={false}
/>
}
@@ -250,6 +265,8 @@ FiresMap.propTypes = {
userSubs: PropTypes.arrayOf(PropTypes.object).isRequired,
activefires: PropTypes.arrayOf(PropTypes.object).isRequired,
firealerts: PropTypes.arrayOf(PropTypes.object).isRequired,
+ falsePositives: PropTypes.arrayOf(PropTypes.object).isRequired,
+ falsePositivesTotal: PropTypes.number.isRequired,
lastCheck: PropTypes.instanceOf(Date),
activefirestotal: PropTypes.number.isRequired,
center: PropTypes.arrayOf(PropTypes.number),
@@ -283,14 +300,23 @@ export default translate([], { wait: true })(withTracker(() => {
mapSize.get()[1].lng,
mapSize.get()[1].lat
);
+ Meteor.subscribe(
+ 'falsePositivesMyloc',
+ mapSize.get()[0].lng,
+ mapSize.get()[0].lat,
+ mapSize.get()[1].lng,
+ mapSize.get()[1].lat
+ );
}
});
Meteor.subscribe('activefirestotal');
+ Meteor.subscribe('falsePositivesTotal');
Meteor.subscribe('settings');
const userSubs = Meteor.subscribe('userSubsToFires');
const lastCheck = SiteSettings.findOne({ name: 'last-fire-check' });
const fireAlerts = FireAlertsCollection.find().fetch();
+ const falsePositives = FalsePositivesCollection.find().fetch();
return {
loading: !subscription ? true : !subscription.ready(),
userSubs: UserSubsToFiresCollection.find().fetch(),
@@ -298,7 +324,9 @@ export default translate([], { wait: true })(withTracker(() => {
// Not reactive query depending on zoom level
activefires: ActiveFiresCollection.find({}, { reactive: zoom.get() >= MAXZOOMREACTIVE }).fetch(),
activefirestotal: Counter.get('countActiveFires') + fireAlerts.length,
+ falsePositivesTotal: Counter.get('countFalsePositives') + fireAlerts.length,
firealerts: fireAlerts,
+ falsePositives,
lastCheck: lastCheck ? lastCheck.value : null,
center: geolocation.get(),
zoom: zoom.get()
diff --git a/private/pages/credits-en.md b/private/pages/credits-en.md
index 049f3b5..185c5fd 100644
--- a/private/pages/credits-en.md
+++ b/private/pages/credits-en.md
@@ -12,6 +12,7 @@ We acknowledge the use of data and imagery from LANCE FIRMS operated by the NASA
* Home slide secondary 2: Smog over Almaty city, Kazakhstan, January 2014, photo by Igors Jefimovs, CC-BY 3.0, [source](https://commons.wikimedia.org/wiki/File:Smog_over_Almaty.jpg)
* Wildfire home photo: [California Wildfires, October 23th 2007](https://commons.wikimedia.org/wiki/File:California_Wildfires_October_23_2007.jpg), [fuente](http://www.nasa.gov/vision/earth/lookingatearth/socal_wildfires_oct07.html).
* Default avatar: A colored Emoji from Noto project, released under Apache license, [source](https://commons.wikimedia.org/wiki/File:Emoji_u1f469_1f3fd_200d_1f692.svg).
+* Industry fire marker by Nicolas Mollet, released under CC-BY-SA 3.0, [source](https://mapicons.mapsmarker.com/category/markers/industry/).
**Others**
diff --git a/private/pages/credits-es.md b/private/pages/credits-es.md
index b1ada88..3673589 100644
--- a/private/pages/credits-es.md
+++ b/private/pages/credits-es.md
@@ -12,6 +12,7 @@ Reconocemos el uso de datos e imágenes de LANCE FIRMS operadas por NASA/GSFC/Ea
* Diapositiva secundaria 2 del Inicio: Polución en la ciudad de Almaty, Kazakhstan, enero de 2014, foto de Igors Jefimovs, CC-BY 3.0, [source](https://commons.wikimedia.org/wiki/File:Smog_over_Almaty.jpg)
* Foto de inicio de fuego forestal: [Incendios forestales de California, 23 de octubre de 2007](https://commons.wikimedia.org/wiki/File:California_Wildfires_October_23_2007.jpg), [fuente](http://www.nasa.gov/vision/earth/lookingatearth/socal_wildfires_oct07.html).
* Avatar predeterminado: un Emoji coloreado del proyecto Noto, publicado bajo licencia Apache, [fuente](https://commons.wikimedia.org/wiki/File:Emoji_u1f469_1f3fd_200d_1f692.svg).
+* Marcador de industia por Nicolas Mollet, publicado bajo CC-BY-SA 3.0, [fuente](https://mapicons.mapsmarker.com/category/markers/industry/).
**Otros**
diff --git a/public/industry-marker.png b/public/industry-marker.png
new file mode 100644
index 0000000000000000000000000000000000000000..2babdab5aff63c3735394aeeab85d2be9e814079
GIT binary patch
literal 339
zcmeAS@N?(olHy`uVBq!ia0vp^3P7yN!3HFix4(90U|^K;ba4!cXiZL#U|pOb($f@Z
zz~y#OLn7gT2eVxXk9Xwq
z4gCOAyBVb6{@zEoF8>$!@Ds#YzHa}|&-OwyHhvVFvsP^L%l%Pjmi>RhDJ1)Vw-G44
zzD+7YB%-<_@!K@(AdvCv?f*P5^tpM=pzy7)85UU;+7
zxcWfMJoA@4htFL;|L{?snh?+Kb9)*-_g-z0ICrm#{rUZvH>VFAIKXh8(QA?o&+XkZ
zg=wyP_!QKn1)1~oz8!tpz<8cj=ZIDdt1dIMN(JY~PF7|)Cgyo;Zi^EREKVqBX9(3j
fbXi04CIiEh3G4W$WP}z21B=1a)z4*}Q$iB}iAj=b
literal 0
HcmV?d00001
From 35ba2158346baa986147a40839fefc868e42ec4b Mon Sep 17 00:00:00 2001
From: vjrj
Date: Thu, 8 Feb 2018 17:44:42 +0100
Subject: [PATCH 004/330] Store center/zoom in fire map
---
imports/ui/pages/FiresMap/FiresMap.js | 17 ++++++++++++-----
package-lock.json | 5 +++++
package.json | 3 ++-
3 files changed, 19 insertions(+), 6 deletions(-)
diff --git a/imports/ui/pages/FiresMap/FiresMap.js b/imports/ui/pages/FiresMap/FiresMap.js
index 802948d..e2b6b8c 100644
--- a/imports/ui/pages/FiresMap/FiresMap.js
+++ b/imports/ui/pages/FiresMap/FiresMap.js
@@ -13,6 +13,7 @@ import { Trans, translate } from 'react-i18next';
import { Map } from 'react-leaflet';
import Control from 'react-leaflet-control';
import _ from 'lodash';
+import store from 'store';
import 'leaflet/dist/leaflet.css';
import 'leaflet-graphicscale/dist/Leaflet.GraphicScale.min.css';
import 'leaflet-graphicscale/dist/Leaflet.GraphicScale.min.js';
@@ -82,7 +83,8 @@ class FiresMap extends React.Component {
const bounds = this.getMap().getBounds();
// console.log(bounds);
mapSize.set([bounds.getNorthEast(), bounds.getSouthWest()]);
- /*
+ store.set('firesmap_center', viewport.center);
+ store.set('firesmap_zoom', viewport.zoom);
if (viewport.center === this.state.viewport.center &&
viewport.zoom === this.state.viewport.zoom) {
// Do nothing, in same point
@@ -90,7 +92,7 @@ class FiresMap extends React.Component {
}
zoom.set(viewport.zoom);
center.set(viewport.center);
- this.setState({ viewport }); */
+ this.setState({ viewport });
}
centerOnUserLocation(viewport) {
@@ -279,9 +281,14 @@ let init = true;
export default translate([], { wait: true })(withTracker(() => {
let subscription;
+
+ const centerStored = store.get('firesmap_center');
+ const zoomStored = store.get('firesmap_zoom');
+ zoom.set(zoomStored || 8);
+
Meteor.autorun(() => {
- if (geolocation.get() && init) {
- center.set(geolocation.get());
+ if ((centerStored || geolocation.get()) && init) {
+ center.set(centerStored || geolocation.get());
// console.log(`Geolocation ${geolocation.get()}`);
init = false;
}
@@ -328,7 +335,7 @@ export default translate([], { wait: true })(withTracker(() => {
firealerts: fireAlerts,
falsePositives,
lastCheck: lastCheck ? lastCheck.value : null,
- center: geolocation.get(),
+ center: center.get(),
zoom: zoom.get()
};
})(FiresMap));
diff --git a/package-lock.json b/package-lock.json
index 0acac3c..eb4a96a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11140,6 +11140,11 @@
"resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
"integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks="
},
+ "store": {
+ "version": "2.0.12",
+ "resolved": "https://registry.npmjs.org/store/-/store-2.0.12.tgz",
+ "integrity": "sha1-jFNOKguDH3K3X8XxEZhXxE711ZM="
+ },
"string-length": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz",
diff --git a/package.json b/package.json
index f99ece8..e517587 100644
--- a/package.json
+++ b/package.json
@@ -69,7 +69,8 @@
"react-router-hash-link": "^1.1.1",
"reactstrap": "^5.0.0-alpha.3",
"simpl-schema": "^0.3.2",
- "simple-line-icons": "^2.4.1"
+ "simple-line-icons": "^2.4.1",
+ "store": "^2.0.12"
},
"devDependencies": {
"babel-jest": "^21.2.0",
From c3bc4e604a0866dc6d8cd45ad82af04034ddf417 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Thu, 8 Feb 2018 20:41:24 +0100
Subject: [PATCH 005/330] Minor fix in firesmap for race condition
---
imports/ui/pages/FiresMap/FiresMap.js | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/imports/ui/pages/FiresMap/FiresMap.js b/imports/ui/pages/FiresMap/FiresMap.js
index e2b6b8c..15850f2 100644
--- a/imports/ui/pages/FiresMap/FiresMap.js
+++ b/imports/ui/pages/FiresMap/FiresMap.js
@@ -103,9 +103,8 @@ class FiresMap extends React.Component {
this.setState({ useMarkers: use });
}
- addScale() {
+ addScale(map) {
// https://www.npmjs.com/package/leaflet-graphicscale
- const map = this.getMap();
const options = {
fill: 'fill',
showSubunits: true
@@ -120,7 +119,7 @@ class FiresMap extends React.Component {
const bounds = lmap.getBounds();
mapSize.set([bounds.getNorthEast(), bounds.getSouthWest()]);
if (!this.state.scaleAdded) {
- this.addScale();
+ this.addScale(lmap);
this.state.scaleAdded = true;
}
} catch (e) {
From 346016b5c43a1f935139c5ed3d7e35e2215de1d9 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Fri, 9 Feb 2018 11:02:21 +0100
Subject: [PATCH 006/330] Added sitemap
---
.meteor/packages | 1 +
.meteor/versions | 2 +
cucumber/features/general.feature | 14 +++++++
cucumber/features/pages.feature | 1 -
.../features/steps_definitions/general.js | 20 ++++++++++
imports/startup/server/index.js | 1 +
imports/startup/server/sitemaps.js | 40 +++++++++++++++++++
7 files changed, 78 insertions(+), 1 deletion(-)
create mode 100644 cucumber/features/general.feature
create mode 100644 cucumber/features/steps_definitions/general.js
create mode 100644 imports/startup/server/sitemaps.js
diff --git a/.meteor/packages b/.meteor/packages
index 4b3f213..de1e606 100644
--- a/.meteor/packages
+++ b/.meteor/packages
@@ -61,3 +61,4 @@ reywood:publish-composite
barbatus:stars-rating
arkham:comments-ui
facts
+gadicohen:sitemaps
diff --git a/.meteor/versions b/.meteor/versions
index ce2ba16..e9ae166 100644
--- a/.meteor/versions
+++ b/.meteor/versions
@@ -50,6 +50,8 @@ flowkey:raven@1.0.0
fortawesome:fontawesome@4.7.0
fourseven:scss@4.5.4
gadicc:blaze-react-component@1.4.0
+gadicohen:robots-txt@0.0.10
+gadicohen:sitemaps@0.0.26
geojson-utils@1.0.10
github-oauth@1.2.0
google-oauth@1.2.4
diff --git a/cucumber/features/general.feature b/cucumber/features/general.feature
new file mode 100644
index 0000000..c3ea6a8
--- /dev/null
+++ b/cucumber/features/general.feature
@@ -0,0 +1,14 @@
+Feature: This app should generate a correct sitemap, etc
+
+ @watch
+ Scenario: I verify that a sitemap is generated correctly
+ Given the page sitemap.xml
+ Then I check that exist this list of pages in the sitemap
+ | license | License |
+ | terms | Terms of Service |
+ | credits | Credits |
+ | privacy | Privacy Policy |
+ | fires | Active Fires |
+ | login | Login |
+ | signup | Signup |
+ # And should be spiderables
diff --git a/cucumber/features/pages.feature b/cucumber/features/pages.feature
index 1946218..1d6ff70 100644
--- a/cucumber/features/pages.feature
+++ b/cucumber/features/pages.feature
@@ -1,4 +1,3 @@
-@watch
Feature: Test all secundary pages
Scenario: Check that all secondary pages work well
diff --git a/cucumber/features/steps_definitions/general.js b/cucumber/features/steps_definitions/general.js
new file mode 100644
index 0000000..33bb272
--- /dev/null
+++ b/cucumber/features/steps_definitions/general.js
@@ -0,0 +1,20 @@
+/* global module expect require process client */
+
+module.exports = function doSteps() {
+ let pages;
+
+ this.Then(/^I check that exist this list of pages in the sitemap$/, (table, callback) => {
+ // Write code here that turns the phrase above into concrete actions
+ pages = table.raw();
+ for (let i = 0; i < pages.length; i += 1) {
+ // TODO download sitemap and parse
+ // client.waitForText('.text', pages[i][0]);
+ }
+ callback();
+ });
+
+ this.Given(/^the page sitemap\.xml$/, (callback) => {
+ client.url(`${process.env.ROOT_URL}/sitemap.xml`);
+ callback();
+ });
+};
diff --git a/imports/startup/server/index.js b/imports/startup/server/index.js
index 8a27f96..aa32e9d 100644
--- a/imports/startup/server/index.js
+++ b/imports/startup/server/index.js
@@ -8,3 +8,4 @@ import './migrations';
import './notificationsObserver';
import './facts';
import '../common/comments';
+import './sitemaps';
diff --git a/imports/startup/server/sitemaps.js b/imports/startup/server/sitemaps.js
new file mode 100644
index 0000000..29a62df
--- /dev/null
+++ b/imports/startup/server/sitemaps.js
@@ -0,0 +1,40 @@
+/* global sitemaps Comments */
+/* eslint-disable import/no-absolute-path */
+
+import Fires from '/imports/api/Fires/Fires';
+// import Comments from '/imports/api/Comments/Comments';
+
+sitemaps.add('/sitemap.xml', () => {
+ const today = new Date();
+
+ const out = [];
+ out.push({ page: '/fires', lastmod: today });
+ out.push({ page: '/login', lastmod: today });
+ out.push({ page: '/signup', lastmod: today });
+ out.push({ page: '/recover-password', lastmod: today });
+ out.push({ page: '/credits', lastmod: today });
+ out.push({ page: '/terms', lastmod: today });
+ out.push({ page: '/license', lastmod: today });
+ out.push({ page: '/privacy', lastmod: today });
+
+
+ // When user has public page
+ /* const users = Meteor.users.find().fetch();
+ _.each(users, function(user) {
+ out.push({
+ page: '/persona/' + user.username,
+ lastmod: user.updatedAt
+ });
+ }); */
+
+ Fires.find().fetch().forEach((fire) => {
+ // Search the last comment of tha fire
+ const lastComment = Comments.getCollection().findOne({ referenceId: `fire-${fire._id}` }, { sort: { createdAt: -1 } });
+ out.push({
+ page: `/fire/archive/${fire._id._str}`,
+ lastmod: lastComment ? lastComment.createdAt : fire.updatedAt
+ });
+ });
+
+ return out;
+});
From 97eb8c91f57e766f43d4ef1bf532c33f27b4a0b1 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Fri, 9 Feb 2018 11:21:14 +0100
Subject: [PATCH 007/330] Upgrade leaflet
---
package-lock.json | 45 ++++++++++++++++++++++++++++++++++-----------
package.json | 4 ++--
2 files changed, 36 insertions(+), 13 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index eb4a96a..002d5c7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7409,9 +7409,9 @@
}
},
"leaflet": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.2.0.tgz",
- "integrity": "sha512-Bold8phAE6WcRsuwhofrQ7cOK1REFHaYIkKuj7+TBYK3ONKRpGGIb5oXR5akYotFnrWN0TWKh6Svlhflm3dogg=="
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.3.1.tgz",
+ "integrity": "sha512-adQOIzh+bfdridLM1xIgJ9VnJbAUY3wqs/ueF+ITla+PLQ1z47USdBKUf+iD9FuUA8RtlT6j6hZBfZoA6mW+XQ=="
},
"leaflet-geodesy": {
"version": "0.2.1",
@@ -7529,9 +7529,9 @@
"integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4="
},
"lodash-es": {
- "version": "4.17.4",
- "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.4.tgz",
- "integrity": "sha1-3MHXVS4VCgZABzupyzHXDwMpUOc="
+ "version": "4.17.5",
+ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.5.tgz",
+ "integrity": "sha512-Ez3ONp3TK9gX1HYKp6IhetcVybD+2F+Yp6GS9dfH8ue6EOCEzQtQEh4K0FYWBP9qLv+lzeQAYXw+3ySfxyZqkw=="
},
"lodash._getnative": {
"version": "3.9.1",
@@ -10003,13 +10003,36 @@
}
},
"react-leaflet": {
- "version": "1.7.8",
- "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-1.7.8.tgz",
- "integrity": "sha512-SEN/QJw8QxOVcxax0D0lqYEYO9IWwn78+laatA+IGaY6H42yD5IxlMcQh9HZGV4NQqs0NPnbc6FW2bHZZIldJA==",
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-1.8.0.tgz",
+ "integrity": "sha512-/2mm/ZQodvcknMJpHm0UMbsQIeQVe9TNA62TUYzTWEX6yAqfLxjQmKIio0xKWSoMIRuQ0R1lROvVZFgiHT7KNw==",
"requires": {
"lodash": "4.17.4",
- "lodash-es": "4.17.4",
- "warning": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz"
+ "lodash-es": "4.17.5",
+ "warning": "3.0.0"
+ },
+ "dependencies": {
+ "js-tokens": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
+ "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls="
+ },
+ "loose-envify": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz",
+ "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=",
+ "requires": {
+ "js-tokens": "3.0.2"
+ }
+ },
+ "warning": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz",
+ "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=",
+ "requires": {
+ "loose-envify": "1.3.1"
+ }
+ }
}
},
"react-leaflet-control": {
diff --git a/package.json b/package.json
index e517587..945775d 100644
--- a/package.json
+++ b/package.json
@@ -33,7 +33,7 @@
"jquery": "^2.2.4",
"jquery-validation": "^1.17.0",
"juice": "^4.2.2",
- "leaflet": "^1.2.0",
+ "leaflet": "^1.3.1",
"leaflet-geodesy": "^0.2.1",
"leaflet-graphicscale": "0.0.2",
"leaflet-sleep": "^0.5.1",
@@ -58,7 +58,7 @@
"react-dom": "^16.0.0",
"react-helmet": "^5.2.0",
"react-i18next": "^7.1.1",
- "react-leaflet": "^1.7.7",
+ "react-leaflet": "^1.8.0",
"react-leaflet-control": "^1.4.0",
"react-leaflet-google": "^3.2.1",
"react-leaflet-sidebarv2": "^0.5.1",
From 6a9f3e4df66e22eec6d700c2b0eb9d14ecb39ba6 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Sun, 11 Feb 2018 19:22:34 +0100
Subject: [PATCH 008/330] English typo
---
client/main.html | 4 ++--
public/locales/en/common.json | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/client/main.html b/client/main.html
index bf46507..90fe4b1 100644
--- a/client/main.html
+++ b/client/main.html
@@ -1,7 +1,7 @@
- All agaist Fire | Tod@s contra el Fuego
-
+ All against Fire | Tod@s contra el Fuego
+
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index 00a2224..6b383de 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -1,7 +1,7 @@
{
"AppName": "All Against Fire",
"AppNameFull": "All Against Fire!",
- "AppDescrip": "Corwdsourcing against wildfires",
+ "AppDescrip": "Crowdsourcing against wildfires",
"AppDescripLong": "We use different sources of data to notify you of active fires in your areas of interest",
"OrgName": "Comunes",
"OrgNameFull": "Comunes Association",
From e46ae3a000b5b9a98045bd5f3e9506104289dc7d Mon Sep 17 00:00:00 2001
From: vjrj
Date: Mon, 12 Feb 2018 09:49:13 +0100
Subject: [PATCH 009/330] Added zones nav link
---
imports/ui/components/Navigation/Navigation.js | 6 +++++-
imports/ui/layouts/App/App.js | 2 ++
imports/ui/pages/Subscriptions/SubscriptionsMap.js | 8 ++++++++
public/locales/es/common.json | 4 +++-
4 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/imports/ui/components/Navigation/Navigation.js b/imports/ui/components/Navigation/Navigation.js
index 4207ee1..f252702 100644
--- a/imports/ui/components/Navigation/Navigation.js
+++ b/imports/ui/components/Navigation/Navigation.js
@@ -15,8 +15,9 @@ import NavItem from '../NavItem/NavItem';
import './Navigation.scss';
// removed class: fixed-top
+// md instead of lg: no menu in medium
const Navigation = props => (
-
+
{/*
*/}
@@ -49,6 +50,9 @@ const Navigation = props => (
{props.t('activeFires')}
+
+ {props.t('Zonas vigiladas')}
+
{!props.authenticated ? : }
{/* */}
diff --git a/imports/ui/layouts/App/App.js b/imports/ui/layouts/App/App.js
index 4e3ff27..3cb8759 100644
--- a/imports/ui/layouts/App/App.js
+++ b/imports/ui/layouts/App/App.js
@@ -44,6 +44,7 @@ import Terms from '../../pages/Terms/Terms';
import Privacy from '../../pages/Privacy/Privacy';
import License from '../../pages/License/License';
import Credits from '../../pages/Credits/Credits';
+import ZonesMap from '../../pages/ZonesMap/ZonesMap';
import ReSendEmail from '../../components/ReSendEmail/ReSendEmail';
import ErrorBoundary from '../../components/ErrorBoundary/ErrorBoundary';
import history from '../../components/History/History';
@@ -83,6 +84,7 @@ const App = props => (
+
diff --git a/imports/ui/pages/Subscriptions/SubscriptionsMap.js b/imports/ui/pages/Subscriptions/SubscriptionsMap.js
index ba7b434..0afd38d 100644
--- a/imports/ui/pages/Subscriptions/SubscriptionsMap.js
+++ b/imports/ui/pages/Subscriptions/SubscriptionsMap.js
@@ -9,6 +9,7 @@ import { Meteor } from 'meteor/meteor';
import { withTracker } from 'meteor/react-meteor-data';
import { Trans, translate } from 'react-i18next';
import { Map } from 'react-leaflet';
+import { Helmet } from 'react-helmet';
import 'leaflet/dist/leaflet.css';
import 'leaflet-graphicscale/dist/Leaflet.GraphicScale.min.css';
import 'leaflet-graphicscale/dist/Leaflet.GraphicScale.min.js';
@@ -20,6 +21,7 @@ import DefMapLayers from '/imports/ui/components/Maps/DefMapLayers';
import Loading from '/imports/ui/components/Loading/Loading';
import UserSubsToFiresCollection from '/imports/api/Subscriptions/Subscriptions';
import { isChrome } from '/imports/ui/components/Utils/isMobile';
+import { isHome } from '/imports/ui/components/Utils/location';
import './SubscriptionsMap.scss';
@@ -82,9 +84,15 @@ class SubscriptionsMap extends React.Component {
}
render() {
+ const { t } = this.props;
console.log(`Rendering Subs users ready ${this.props.subsready} subs: ${this.props.userSubs.length} viewport: ${JSON.stringify(this.state.viewport)}`);
return (
+ { !isHome() &&
+
+ {t('AppName')}: {t('Zonas vigiladas')}
+
+ }
{!this.props.subsready ?
diff --git a/public/locales/es/common.json b/public/locales/es/common.json
index db61fc1..0231e6e 100644
--- a/public/locales/es/common.json
+++ b/public/locales/es/common.json
@@ -267,5 +267,7 @@
"nuestros usuarios/as": "nuestros usuarios/as",
"Pulsa para más información": "Pulsa para más información",
"Detectado":
- "Detectado"
+ "Detectado",
+ "Zonas vigiladas por nuestros usuari@s actualmente":
+ "Zonas vigiladas por nuestros usuari@s actualmente"
}
From 31e8a7dfdd1329b30c71f21d4dc21de04cfb57b1 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Mon, 12 Feb 2018 12:17:08 +0100
Subject: [PATCH 010/330] Minor footer style fix
---
imports/ui/components/Footer/Footer.scss | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/imports/ui/components/Footer/Footer.scss b/imports/ui/components/Footer/Footer.scss
index 7a0db88..d74554e 100644
--- a/imports/ui/components/Footer/Footer.scss
+++ b/imports/ui/components/Footer/Footer.scss
@@ -79,4 +79,13 @@ body {
.Footer, .Footer p {
font-size: 13px;
}
-}
\ No newline at end of file
+
+ .Footer {
+ line-height: 1.8em;
+ }
+
+ .Footer ul li:first-child,
+ .Footer ul li:nth-child(2) {
+ margin-right: 7px;
+ }
+}
From ac0fa6cebd8ea07812cad9f6de36df7d252cb140 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Mon, 12 Feb 2018 12:17:24 +0100
Subject: [PATCH 011/330] Refactor
---
imports/ui/layouts/App/App.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/imports/ui/layouts/App/App.js b/imports/ui/layouts/App/App.js
index 3cb8759..9a11c69 100644
--- a/imports/ui/layouts/App/App.js
+++ b/imports/ui/layouts/App/App.js
@@ -39,6 +39,9 @@ import RecoverPassword from '../../pages/RecoverPassword/RecoverPassword';
import ResetPassword from '../../pages/ResetPassword/ResetPassword';
import Profile from '../../pages/Profile/Profile';
import NotFound from '../../pages/NotFound/NotFound';
+import FiresMap from '../../pages/FiresMap/FiresMap';
+import Fires from '../../pages/Fires/Fires';
+import Sandbox from '../../pages/Sandbox/Sandbox';
import Footer from '../../components/Footer/Footer';
import Terms from '../../pages/Terms/Terms';
import Privacy from '../../pages/Privacy/Privacy';
@@ -49,9 +52,6 @@ import ReSendEmail from '../../components/ReSendEmail/ReSendEmail';
import ErrorBoundary from '../../components/ErrorBoundary/ErrorBoundary';
import history from '../../components/History/History';
import '../../components/NotificationsObserver/NotificationsObserver';
-import FiresMap from '../../pages/FiresMap/FiresMap';
-import Fires from '../../pages/Fires/Fires';
-import Sandbox from '../../pages/Sandbox/Sandbox';
import './App.scss';
const App = props => (
From 28d8f695c1af16b56dd64c7f47a7e2e36a92ccaf Mon Sep 17 00:00:00 2001
From: vjrj
Date: Mon, 12 Feb 2018 12:18:40 +0100
Subject: [PATCH 012/330] Added zones map nav link
---
imports/ui/pages/ZonesMap/ZonesMap.js | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
create mode 100644 imports/ui/pages/ZonesMap/ZonesMap.js
diff --git a/imports/ui/pages/ZonesMap/ZonesMap.js b/imports/ui/pages/ZonesMap/ZonesMap.js
new file mode 100644
index 0000000..c5a0c8f
--- /dev/null
+++ b/imports/ui/pages/ZonesMap/ZonesMap.js
@@ -0,0 +1,25 @@
+/* eslint-disable import/no-absolute-path */
+import React, { Fragment } from 'react';
+// import PropTypes from 'prop-types';
+import { translate } from 'react-i18next';
+import SubscriptionsMap from '/imports/ui/pages/Subscriptions/SubscriptionsMap';
+
+class ZonesMap extends React.Component {
+ componentDidMount() {
+ // this.setState({init: true});
+ }
+
+ render() {
+ return (
+
+
+
+ );
+ }
+}
+
+ZonesMap.propTypes = {
+ // history: PropTypes.object.isRequired
+};
+
+export default translate([], { wait: true })(ZonesMap);
From cd80d72e328a7152c67a1bc925900cecc221ef70 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Mon, 12 Feb 2018 12:18:50 +0100
Subject: [PATCH 013/330] Added share it
---
imports/ui/components/ShareIt/ShareIt.js | 146 ++++++++++++++++++
imports/ui/components/ShareIt/ShareIt.scss | 19 +++
imports/ui/components/Utils/location.js | 8 +
imports/ui/pages/Fires/Fires.js | 3 +
imports/ui/pages/FiresMap/FiresMap.js | 7 +-
imports/ui/pages/Index/Index.js | 12 +-
.../pages/Subscriptions/SubscriptionsMap.js | 7 +-
package-lock.json | 23 ++-
package.json | 1 +
9 files changed, 219 insertions(+), 7 deletions(-)
create mode 100644 imports/ui/components/ShareIt/ShareIt.js
create mode 100644 imports/ui/components/ShareIt/ShareIt.scss
diff --git a/imports/ui/components/ShareIt/ShareIt.js b/imports/ui/components/ShareIt/ShareIt.js
new file mode 100644
index 0000000..558e216
--- /dev/null
+++ b/imports/ui/components/ShareIt/ShareIt.js
@@ -0,0 +1,146 @@
+/* eslint-disable react/jsx-indent-props */
+/* eslint-disable import/no-absolute-path */
+/* eslint-disable import/no-absolute-path */
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { translate } from 'react-i18next';
+import { withTracker } from 'meteor/react-meteor-data';
+import { currentLocationHref } from '/imports/ui/components/Utils/location';
+import {
+ FacebookShareButton,
+ GooglePlusShareButton,
+ TwitterShareButton,
+ TelegramShareButton,
+ WhatsappShareButton,
+ RedditShareButton,
+ EmailShareButton,
+
+ FacebookIcon,
+ GooglePlusIcon,
+ TwitterIcon,
+ TelegramIcon,
+ WhatsappIcon,
+ RedditIcon,
+ EmailIcon
+} from 'react-share';
+
+import './ShareIt.scss';
+
+class ShareIt extends Component {
+ constructor(props) {
+ super(props);
+ this.t = props.t;
+ }
+
+ render() {
+ const { title } = this.props;
+ const shareUrl = currentLocationHref();
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+ShareIt.propTypes = {
+ t: PropTypes.func.isRequired,
+ title: PropTypes.string.isRequired
+};
+
+ShareIt.defaultProps = {
+};
+
+export default translate([], { wait: true })(withTracker((props) => {
+ const { title } = props;
+ return {
+ title
+ };
+})(ShareIt));
diff --git a/imports/ui/components/ShareIt/ShareIt.scss b/imports/ui/components/ShareIt/ShareIt.scss
new file mode 100644
index 0000000..99996de
--- /dev/null
+++ b/imports/ui/components/ShareIt/ShareIt.scss
@@ -0,0 +1,19 @@
+.share_it {
+ vertical-align: top;
+ display: inline-block;
+ margin-right: 10px;
+ text-align: center;
+}
+
+.share_it__share-count {
+ margin-top: 3px;
+ font-size: 12px;
+}
+
+.share_it__share-button {
+ cursor: pointer;
+}
+
+.share_it__share-button:hover:not(:active) {
+ opacity: 0.75;
+}
diff --git a/imports/ui/components/Utils/location.js b/imports/ui/components/Utils/location.js
index 8f60415..804724a 100644
--- a/imports/ui/components/Utils/location.js
+++ b/imports/ui/components/Utils/location.js
@@ -10,4 +10,12 @@ export const currentLocation = () => {
return location.get();
};
+export const currentLocationHref = () => {
+ if (Meteor.isClient) {
+ return window.location.href;
+ }
+ // FIXME
+ return location.get();
+};
+
export const isHome = () => currentLocation() === '/';
diff --git a/imports/ui/pages/Fires/Fires.js b/imports/ui/pages/Fires/Fires.js
index 52d1b81..7b7f425 100644
--- a/imports/ui/pages/Fires/Fires.js
+++ b/imports/ui/pages/Fires/Fires.js
@@ -19,6 +19,7 @@ import FromNow from '/imports/ui/components/FromNow/FromNow';
import { dateLongFormat } from '/imports/api/Common/dates';
import '/imports/startup/client/comments';
import FalsePositiveTypes from '/imports/api/FalsePositives/FalsePositiveTypes';
+import ShareIt from '/imports/ui/components/ShareIt/ShareIt';
import './Fires.scss';
class Fire extends React.Component {
@@ -113,6 +114,8 @@ class Fire extends React.Component {
Fuego notificado por uno de nuestros usuarios/as
}
+
+
{(fire.type !== 'vecinal') &&
{t('¿No es un fuego forestal?')}
diff --git a/imports/ui/pages/FiresMap/FiresMap.js b/imports/ui/pages/FiresMap/FiresMap.js
index 15850f2..8cbfb45 100644
--- a/imports/ui/pages/FiresMap/FiresMap.js
+++ b/imports/ui/pages/FiresMap/FiresMap.js
@@ -32,6 +32,7 @@ import SiteSettings from '/imports/api/SiteSettings/SiteSettings';
import UserSubsToFiresCollection from '/imports/api/Subscriptions/Subscriptions';
import { isNotHomeAndMobile, isChrome } from '/imports/ui/components/Utils/isMobile';
import { isHome } from '/imports/ui/components/Utils/location';
+import ShareIt from '/imports/ui/components/ShareIt/ShareIt';
import './FiresMap.scss';
@@ -137,6 +138,7 @@ class FiresMap extends React.Component {
render() {
const { t } = this.props;
console.log(`Rendering ${this.props.loading ? 'loading' : 'LOADED'} map ${this.props.activefires.length + this.props.firealerts.length} of ${this.props.activefirestotal} total. False positives: ${this.props.falsePositives.length}. Subs users ready ${this.props.subsready} (${this.props.userSubs.length}), reactive ${this.state.viewport.zoom >= MAXZOOMREACTIVE}`);
+ const title = `${t('AppName')}: ${t('Fuegos activos')}`;
if (Meteor.isDevelopment) {
console.log(`False positives total: ${this.props.falsePositivesTotal}`);
}
@@ -148,7 +150,7 @@ class FiresMap extends React.Component {
>
{ !isHome() &&
- {t('AppName')}: {t('Fuegos activos')}
+ {title}
}
{this.props.loading || !this.props.subsready ?
@@ -255,6 +257,9 @@ class FiresMap extends React.Component {
(*) Para preservar la privacidad de nuestros usuarios/as, los datos reflejados están aleatoriamente alterados y son solo orientativos.
+ { !isHome() &&
+
+ }
);
}
diff --git a/imports/ui/pages/Index/Index.js b/imports/ui/pages/Index/Index.js
index 5a3fe23..f69ec43 100644
--- a/imports/ui/pages/Index/Index.js
+++ b/imports/ui/pages/Index/Index.js
@@ -16,6 +16,7 @@ import 'bootstrap-carousel-swipe-haven/carousel-swipe';
import { isAnyMobile } from '/imports/ui/components/Utils/isMobile';
import SubscriptionEditor from '/imports/ui/components/SubscriptionEditor/SubscriptionEditor';
import SubscriptionsMap from '/imports/ui/pages/Subscriptions/SubscriptionsMap';
+import ShareIt from '/imports/ui/components/ShareIt/ShareIt';
import FiresMap from '../FiresMap/FiresMap';
import './Index.scss';
@@ -35,7 +36,10 @@ class Index extends Component {
}
componentDidMount() {
- $('.carousel').carousel();
+ const c = $('.carousel');
+ if (c.carousel) {
+ c.carousel();
+ }
}
onResize() {
@@ -78,10 +82,11 @@ class Index extends Component {
render() {
const { t } = this.props;
+ const title = `${t('AppName')}: ${t('Inicio')}`;
return (
- {t('AppName')}: {t('Inicio')}
+ {title}
{/* https://v4-alpha.getbootstrap.com/components/carousel/ */}
@@ -291,6 +296,9 @@ class Index extends Component {
+
+
+
diff --git a/imports/ui/pages/Subscriptions/SubscriptionsMap.js b/imports/ui/pages/Subscriptions/SubscriptionsMap.js
index 0afd38d..72842a3 100644
--- a/imports/ui/pages/Subscriptions/SubscriptionsMap.js
+++ b/imports/ui/pages/Subscriptions/SubscriptionsMap.js
@@ -22,6 +22,7 @@ import Loading from '/imports/ui/components/Loading/Loading';
import UserSubsToFiresCollection from '/imports/api/Subscriptions/Subscriptions';
import { isChrome } from '/imports/ui/components/Utils/isMobile';
import { isHome } from '/imports/ui/components/Utils/location';
+import ShareIt from '/imports/ui/components/ShareIt/ShareIt';
import './SubscriptionsMap.scss';
@@ -85,12 +86,13 @@ class SubscriptionsMap extends React.Component {
render() {
const { t } = this.props;
+ const title = `${t('AppName')}: ${t('Zonas vigiladas')}`;
console.log(`Rendering Subs users ready ${this.props.subsready} subs: ${this.props.userSubs.length} viewport: ${JSON.stringify(this.state.viewport)}`);
return (
{ !isHome() &&
- {t('AppName')}: {t('Zonas vigiladas')}
+ {title}
}
{!this.props.subsready ?
@@ -140,6 +142,9 @@ class SubscriptionsMap extends React.Component {
(*) Para preservar la privacidad de nuestros usuarios/as, los datos reflejados están aleatoriamente alterados y son solo orientativos.
+ { !isHome() &&
+
+ }
);
}
diff --git a/package-lock.json b/package-lock.json
index 002d5c7..5f4f4a3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2946,7 +2946,6 @@
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "dev": true,
"requires": {
"ms": "2.0.0"
},
@@ -2954,8 +2953,7 @@
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
- "dev": true
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}
}
},
@@ -7330,6 +7328,14 @@
"integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=",
"dev": true
},
+ "jsonp": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/jsonp/-/jsonp-0.2.1.tgz",
+ "integrity": "sha1-pltPoPEL2nGaBUQep7lMVfPhW64=",
+ "requires": {
+ "debug": "2.6.9"
+ }
+ },
"jsonpointer": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz",
@@ -10205,6 +10211,17 @@
"prop-types": "15.6.0"
}
},
+ "react-share": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/react-share/-/react-share-2.0.0.tgz",
+ "integrity": "sha512-WlY2nPEg8D3X9/WIJwKcEAlfqNVXbvYJ2nXIjRRQR3GBehQCs1iOrEeRn0KxJxj8AOWmX7k/AmJZLvxtovjVdQ==",
+ "requires": {
+ "babel-runtime": "6.26.0",
+ "classnames": "2.2.5",
+ "jsonp": "0.2.1",
+ "prop-types": "15.6.0"
+ }
+ },
"react-side-effect": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-1.1.3.tgz",
diff --git a/package.json b/package.json
index 945775d..e63515b 100644
--- a/package.json
+++ b/package.json
@@ -67,6 +67,7 @@
"react-router-bootstrap": "^0.24.4",
"react-router-dom": "^4.2.2",
"react-router-hash-link": "^1.1.1",
+ "react-share": "^2.0.0",
"reactstrap": "^5.0.0-alpha.3",
"simpl-schema": "^0.3.2",
"simple-line-icons": "^2.4.1",
From 966b2d56b928242a21ac6370bca0176523dc0e95 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Mon, 12 Feb 2018 13:06:06 +0100
Subject: [PATCH 014/330] Save missing fallback
---
imports/startup/client/i18n.js | 7 +++----
imports/startup/common/i18n.js | 1 +
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/imports/startup/client/i18n.js b/imports/startup/client/i18n.js
index d4e3fcc..42c0ab3 100644
--- a/imports/startup/client/i18n.js
+++ b/imports/startup/client/i18n.js
@@ -53,11 +53,10 @@ i18nOpts.react = {
nsMode: 'default' */
};
-const sendMissing = Meteor.isDevelopment;
-if (sendMissing && !Meteor.isProduction) {
+const sendMissing = false; // Meteor.isDevelopment;
+if (sendMissing && Meteor.isDevelopment) {
i18nOpts.sendMissing = true;
- i18nOpts.sendMissingTo = 'fallback';
- i18nOpts.missingKeyHandler = function miss(lng, ns, key, defaultValue, lngs) {
+ i18nOpts.missingKeyHandler = function miss(lng, ns, key, defaultValue) {
Meteor.call('utility.saveMissingI18n', key, defaultValue);
};
}
diff --git a/imports/startup/common/i18n.js b/imports/startup/common/i18n.js
index 2db4371..e72a81f 100644
--- a/imports/startup/common/i18n.js
+++ b/imports/startup/common/i18n.js
@@ -22,6 +22,7 @@ const i18nOpts = {
backend: backOpts,
// lng: 'es',
fallbackLng: ['es', 'en'],
+ sendMissingTo: 'fallback',
interpolation: {
escapeValue: false, // not needed for react!!
formatSeparator: ',',
From b6963ff290176c6956e7fcf83ce4ce87531fa2fe Mon Sep 17 00:00:00 2001
From: vjrj
Date: Mon, 12 Feb 2018 13:06:18 +0100
Subject: [PATCH 015/330] Logout typo
---
imports/ui/pages/Logout/Logout.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/imports/ui/pages/Logout/Logout.js b/imports/ui/pages/Logout/Logout.js
index 0d0c2f9..87293d8 100644
--- a/imports/ui/pages/Logout/Logout.js
+++ b/imports/ui/pages/Logout/Logout.js
@@ -15,7 +15,7 @@ class Logout extends React.Component {
return (
- {this.props.t('AppName')}: {this.props.t('Logout')}
+ {this.props.t('AppName')}: {this.props.t('Cerrar sesión')}
Gracias por Participar
También puedes seguirnos en la web
From fde04bb77f65b29bdd671806349927397e543c48 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Mon, 12 Feb 2018 13:07:09 +0100
Subject: [PATCH 016/330] FiresMap issue with init
---
imports/ui/pages/FiresMap/FiresMap.js | 246 +++++++++++++-------------
1 file changed, 124 insertions(+), 122 deletions(-)
diff --git a/imports/ui/pages/FiresMap/FiresMap.js b/imports/ui/pages/FiresMap/FiresMap.js
index 8cbfb45..ffbf2f2 100644
--- a/imports/ui/pages/FiresMap/FiresMap.js
+++ b/imports/ui/pages/FiresMap/FiresMap.js
@@ -81,19 +81,21 @@ class FiresMap extends React.Component {
handleViewportChange(viewport) {
console.log(`Viewport changed: ${JSON.stringify(viewport)}`);
- const bounds = this.getMap().getBounds();
- // console.log(bounds);
- mapSize.set([bounds.getNorthEast(), bounds.getSouthWest()]);
- store.set('firesmap_center', viewport.center);
- store.set('firesmap_zoom', viewport.zoom);
- if (viewport.center === this.state.viewport.center &&
- viewport.zoom === this.state.viewport.zoom) {
- // Do nothing, in same point
- return;
+ if (this.fireMap) {
+ const bounds = this.getMap().getBounds();
+ // console.log(bounds);
+ mapSize.set([bounds.getNorthEast(), bounds.getSouthWest()]);
+ store.set('firesmap_center', viewport.center);
+ store.set('firesmap_zoom', viewport.zoom);
+ if (viewport.center === this.state.viewport.center &&
+ viewport.zoom === this.state.viewport.zoom) {
+ // Do nothing, in same point
+ return;
+ }
+ zoom.set(viewport.zoom);
+ center.set(viewport.center);
+ this.setState({ viewport });
}
- zoom.set(viewport.zoom);
- center.set(viewport.center);
- this.setState({ viewport });
}
centerOnUserLocation(viewport) {
@@ -149,117 +151,117 @@ class FiresMap extends React.Component {
ref={(divElement) => { this.divElement = divElement; }}
>
{ !isHome() &&
-
- {title}
-
- }
- {this.props.loading || !this.props.subsready ?
-
-
-
- : ''}
- Fuegos activos
-
-
-
- { (this.props.activefires.length + this.props.firealerts.length) === 0 ?
- No hay fuegos activos en esta zona del mapa. Hay un total de {{ countTotal: this.props.activefirestotal }} fuegos activos detectados en todo el mundo. Datos actualizados . :
- En rojo, {{ count: this.props.activefires.length + this.props.firealerts.length }} fuegos activos en el mapa. Hay un total de {{ countTotal: this.props.activefirestotal }} fuegos activos detectados en todo el mundo por la NASA. Datos actualizados .
- }
-
- {isNotHomeAndMobile &&
- En naranja, los fuegos notificados por nuestros usuarios/as recientemente.
}
-
-
- {isNotHomeAndMobile &&
+
+ {title}
+
+ }
+ {this.props.loading || !this.props.subsready ?
+
+
+
+ : ''}
+ Fuegos activos
+
+
+
+ { (this.props.activefires.length + this.props.firealerts.length) === 0 ?
+ No hay fuegos activos en esta zona del mapa. Hay un total de {{ countTotal: this.props.activefirestotal }} fuegos activos detectados en todo el mundo. Datos actualizados . :
+ En rojo, {{ count: this.props.activefires.length + this.props.firealerts.length }} fuegos activos en el mapa. Hay un total de {{ countTotal: this.props.activefirestotal }} fuegos activos detectados en todo el mundo por la NASA. Datos actualizados .
+ }
+
+ {isNotHomeAndMobile &&
+ En naranja, los fuegos notificados por nuestros usuarios/as recientemente.
}
+
+
+ {isNotHomeAndMobile &&
+
+ this.setShowSubsUnion(e.target.checked)}>
+ Resaltar en verde el área vigilada por nuestros usuarios/as (*)
+
+ {(this.state.viewport.zoom >= MAXZOOM) &&
+ this.useMarkers(e.target.checked)}>
+ Resaltar los fuegos con un marcador
+ }
+ }
+
+ { this.state.viewport.zoom >= MAXZOOMREACTIVE ?
+ Los fuegos activos se actualizan en tiempo real. :
+ Haga zoom en una zona de su interés si quiere que los fuegos se actualicen en tiempo real.
+ }
+
+
+
+
+ {/* https://github.com/CliffCloud/Leaflet.Sleep */}
+ {
+ this.fireMap = map;
+ this.handleLeafletLoad(map);
+ }}
+ className="firesmap-leaflet-container"
+ animate
+ minZoom={5}
+ center={this.props.center}
+ zoom={this.props.zoom}
+ preferCanvas
+ onClick={this.onClickReset}
+ viewport={this.state.viewport}
+ onViewportChanged={this.onViewportChanged}
+ sleep={isHome() && !isChrome}
+ sleepTime={10750}
+ wakeTime={750}
+ sleepNote
+ hoverToWake={false}
+ wakeMessage={this.props.t('Pulsa para activar')}
+ wakeMessageTouch={this.props.t('Pulsa para activar')}
+ sleepOpacity={0.6}
+ >
+ {/* http://wiki.openstreetmap.org/wiki/Tile_servers */}
+ {!this.props.loading &&
- this.setShowSubsUnion(e.target.checked)}>
- Resaltar en verde el área vigilada por nuestros usuarios/as (*)
-
- {(this.state.viewport.zoom >= MAXZOOM) &&
- this.useMarkers(e.target.checked)}>
- Resaltar los fuegos con un marcador
- }
- }
-
- { this.state.viewport.zoom >= MAXZOOMREACTIVE ?
- Los fuegos activos se actualizan en tiempo real. :
- Haga zoom en una zona de su interés si quiere que los fuegos se actualicen en tiempo real.
- }
-
-
-
-
- {/* https://github.com/CliffCloud/Leaflet.Sleep */}
- {
- this.fireMap = map;
- this.handleLeafletLoad(map);
- }}
- className="firesmap-leaflet-container"
- animate
- minZoom={5}
- center={this.props.center}
- zoom={this.props.zoom}
- preferCanvas
- onClick={this.onClickReset}
- viewport={this.state.viewport}
- onViewportChanged={this.onViewportChanged}
- sleep={isHome() && !isChrome}
- sleepTime={10750}
- wakeTime={750}
- sleepNote
- hoverToWake={false}
- wakeMessage={this.props.t('Pulsa para activar')}
- wakeMessageTouch={this.props.t('Pulsa para activar')}
- sleepOpacity={0.6}
- >
- {/* http://wiki.openstreetmap.org/wiki/Tile_servers */}
- {!this.props.loading &&
-
- = MAXZOOM}
- useMarkers={this.state.useMarkers}
- nasa={false}
- falsePositives
- />
- = MAXZOOM}
- useMarkers={this.state.useMarkers}
- nasa
- falsePositives={false}
- />
-
- }
-
-
-
- this.centerOnUserLocation(viewport)} onlyIcon {... this.props} />
-
-
-
-
-
- (*) Para preservar la privacidad de nuestros usuarios/as, los datos reflejados están aleatoriamente alterados y son solo orientativos.
-
-
- { !isHome() &&
-
- }
+ = MAXZOOM}
+ useMarkers={this.state.useMarkers}
+ nasa={false}
+ falsePositives
+ />
+ = MAXZOOM}
+ useMarkers={this.state.useMarkers}
+ nasa
+ falsePositives={false}
+ />
+
+ }
+
+
+
+ this.centerOnUserLocation(viewport)} onlyIcon {... this.props} />
+
+
+
+
+
+ (*) Para preservar la privacidad de nuestros usuarios/as, los datos reflejados están aleatoriamente alterados y son solo orientativos.
+
+
+ { !isHome() &&
+
+ }
);
}
From bbe9b1b40ea1f5d36475aa0a7b42e0bc0bdc8555 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Mon, 12 Feb 2018 13:07:23 +0100
Subject: [PATCH 017/330] Improved tests
---
cucumber/features/pages.feature | 2 ++
cucumber/features/steps_definitions/hooks.js | 4 +++-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/cucumber/features/pages.feature b/cucumber/features/pages.feature
index 1d6ff70..9814434 100644
--- a/cucumber/features/pages.feature
+++ b/cucumber/features/pages.feature
@@ -13,6 +13,8 @@ Feature: Test all secundary pages
Given a list of page urls and contents
| login | Login | true |
| signup | Sign Up | true |
+ | fires | Fires | true |
+ | zones | Monitored | true |
| recover-password | Recover Password | true |
| verify-email/something | Verify | true |
| fire/inexistent | This page doesn't exist | false |
diff --git a/cucumber/features/steps_definitions/hooks.js b/cucumber/features/steps_definitions/hooks.js
index ba4c0c5..9e3e611 100644
--- a/cucumber/features/steps_definitions/hooks.js
+++ b/cucumber/features/steps_definitions/hooks.js
@@ -12,7 +12,9 @@
this.waitForEnabled(selector, timeout);
});
this.initMyCmds = true;
- } */
+ } */
+
+ client.windowHandleSize({ width: 1500, height: 1000 });
// server.call('fixtures/reset');
// server.call('emailStub/reset');
From 8a6584acb900f90dc425042c00c4de1b26ec1b92 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Mon, 12 Feb 2018 14:57:13 +0100
Subject: [PATCH 018/330] Fix for participe btn
---
imports/ui/pages/Index/Index.js | 2 +-
imports/ui/pages/Subscriptions/SubscriptionsMap.js | 3 +++
imports/ui/pages/ZonesMap/ZonesMap.js | 11 ++++++-----
3 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/imports/ui/pages/Index/Index.js b/imports/ui/pages/Index/Index.js
index f69ec43..4cfa2f4 100644
--- a/imports/ui/pages/Index/Index.js
+++ b/imports/ui/pages/Index/Index.js
@@ -182,7 +182,7 @@ class Index extends Component {
diff --git a/imports/ui/pages/Subscriptions/SubscriptionsMap.js b/imports/ui/pages/Subscriptions/SubscriptionsMap.js
index 72842a3..ce675fd 100644
--- a/imports/ui/pages/Subscriptions/SubscriptionsMap.js
+++ b/imports/ui/pages/Subscriptions/SubscriptionsMap.js
@@ -81,6 +81,8 @@ class SubscriptionsMap extends React.Component {
const element = document.querySelector('#participe');
if (element) {
element.scrollIntoView();
+ } else {
+ this.props.history.push('/subscriptions');
}
}
@@ -153,6 +155,7 @@ class SubscriptionsMap extends React.Component {
SubscriptionsMap.propTypes = {
subsready: PropTypes.bool.isRequired,
userSubs: PropTypes.arrayOf(PropTypes.object).isRequired,
+ history: PropTypes.object.isRequired,
t: PropTypes.func.isRequired
};
diff --git a/imports/ui/pages/ZonesMap/ZonesMap.js b/imports/ui/pages/ZonesMap/ZonesMap.js
index c5a0c8f..1ad2cad 100644
--- a/imports/ui/pages/ZonesMap/ZonesMap.js
+++ b/imports/ui/pages/ZonesMap/ZonesMap.js
@@ -1,25 +1,26 @@
/* eslint-disable import/no-absolute-path */
import React, { Fragment } from 'react';
-// import PropTypes from 'prop-types';
+import PropTypes from 'prop-types';
import { translate } from 'react-i18next';
import SubscriptionsMap from '/imports/ui/pages/Subscriptions/SubscriptionsMap';
class ZonesMap extends React.Component {
- componentDidMount() {
- // this.setState({init: true});
+ constructor(props) {
+ super(props);
+ this.t = props.t;
}
render() {
return (
-
+
);
}
}
ZonesMap.propTypes = {
- // history: PropTypes.object.isRequired
+ history: PropTypes.object.isRequired
};
export default translate([], { wait: true })(ZonesMap);
From 8e924d1de48e2f33ba42161ab34a08e396b0954a Mon Sep 17 00:00:00 2001
From: vjrj
Date: Mon, 12 Feb 2018 16:59:03 +0100
Subject: [PATCH 019/330] Trying to catch subs union error
---
.../ui/components/Maps/SubsUnion/SubsUnion.js | 21 ++++++++++++-------
1 file changed, 13 insertions(+), 8 deletions(-)
diff --git a/imports/ui/components/Maps/SubsUnion/SubsUnion.js b/imports/ui/components/Maps/SubsUnion/SubsUnion.js
index 4593283..aaee95c 100644
--- a/imports/ui/components/Maps/SubsUnion/SubsUnion.js
+++ b/imports/ui/components/Maps/SubsUnion/SubsUnion.js
@@ -55,14 +55,19 @@ const subsUnion = (union, options) => {
parts: 144
};
options.subs.forEach((sub) => {
- if (sub.location && sub.location.lat && sub.location.lon && sub.distance) {
- check(sub.location.lon, Number);
- check(sub.location.lat, Number);
- check(sub.distance, Number);
- const circle = LGeo.circle([sub.location.lat, sub.location.lon], sub.distance * 1000, copts);
- circle.addTo(unionGroup);
- } else {
- console.error(`Wrong subscription ${JSON.stringify(sub)}`);
+ try {
+ if (sub.location && sub.location.lat && sub.location.lon && sub.distance) {
+ check(sub.location.lon, Number);
+ check(sub.location.lat, Number);
+ check(sub.distance, Number);
+ const circle = LGeo.circle([sub.location.lat, sub.location.lon], sub.distance * 1000, copts);
+ circle.addTo(unionGroup);
+ } else {
+ console.error(`Wrong subscription ${JSON.stringify(sub)}`);
+ }
+ } catch (e) {
+ console.error(e);
+ console.error(`Wrong subscription trying to make union ${JSON.stringify(sub)}`);
}
});
const unionJson = unify(unionGroup.getLayers());
From 5f329f5210540da7ed520b2529076a67a730c28c Mon Sep 17 00:00:00 2001
From: vjrj
Date: Mon, 12 Feb 2018 17:22:46 +0100
Subject: [PATCH 020/330] Logs removed
---
imports/ui/pages/FireSubscription/FireSubscription.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/imports/ui/pages/FireSubscription/FireSubscription.js b/imports/ui/pages/FireSubscription/FireSubscription.js
index 64febbc..34431fe 100644
--- a/imports/ui/pages/FireSubscription/FireSubscription.js
+++ b/imports/ui/pages/FireSubscription/FireSubscription.js
@@ -20,10 +20,10 @@ class FireSubscription extends React.Component {
zoom: this.props.zoom,
distance: this.props.distance
};
- if (props.location) {
+ /* if (props.location) {
console.log(props.location.state);
}
- console.log(this.state);
+ console.log(this.state); */
}
componentDidMount() {
From 47b55267df7b6873f5884ef6eb99d2c2a93423b1 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Mon, 12 Feb 2018 17:23:01 +0100
Subject: [PATCH 021/330] Truncate subs union
---
.../ui/components/Maps/SubsUnion/SubsUnion.js | 8 ++++++--
package-lock.json | 17 +++++++++++++++++
package.json | 1 +
3 files changed, 24 insertions(+), 2 deletions(-)
diff --git a/imports/ui/components/Maps/SubsUnion/SubsUnion.js b/imports/ui/components/Maps/SubsUnion/SubsUnion.js
index aaee95c..e179b33 100644
--- a/imports/ui/components/Maps/SubsUnion/SubsUnion.js
+++ b/imports/ui/components/Maps/SubsUnion/SubsUnion.js
@@ -6,17 +6,21 @@
import { Map } from 'react-leaflet';
import LGeo from 'leaflet-geodesy';
import tunion from '@turf/union';
+import ttrunc from '@turf/truncate';
import { check, Match } from 'meteor/check';
// https://stackoverflow.com/questions/35394577/leaflet-js-union-merge-circles
+const truncOptions = { precision: 6, coordinates: 2 };
function unify(polyList) {
let unionTemp;
for (let i = 0; i < polyList.length; i += 1) {
+ const pol = polyList[i].toGeoJSON();
+ const cleanPol = ttrunc(pol, truncOptions);
if (i === 0) {
- unionTemp = polyList[i].toGeoJSON();
+ unionTemp = cleanPol;
} else {
- unionTemp = tunion(unionTemp, polyList[i].toGeoJSON());
+ unionTemp = tunion(unionTemp, cleanPol);
}
}
return unionTemp;
diff --git a/package-lock.json b/package-lock.json
index 5f4f4a3..5581dfe 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -25,6 +25,23 @@
"resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-5.1.5.tgz",
"integrity": "sha1-FTQFInq5M9AEpbuWQantmZ/L4M8="
},
+ "@turf/meta": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-5.2.0.tgz",
+ "integrity": "sha1-OxrUhe4MOwsXdRMqMsOE1T5LpT0=",
+ "requires": {
+ "@turf/helpers": "5.1.5"
+ }
+ },
+ "@turf/truncate": {
+ "version": "5.1.5",
+ "resolved": "https://registry.npmjs.org/@turf/truncate/-/truncate-5.1.5.tgz",
+ "integrity": "sha1-nu37Oxi6gfLJjT6tCUMcyhiErYk=",
+ "requires": {
+ "@turf/helpers": "5.1.5",
+ "@turf/meta": "5.2.0"
+ }
+ },
"@turf/union": {
"version": "5.1.5",
"resolved": "https://registry.npmjs.org/@turf/union/-/union-5.1.5.tgz",
diff --git a/package.json b/package.json
index e63515b..68b572a 100644
--- a/package.json
+++ b/package.json
@@ -8,6 +8,7 @@
"dependencies": {
"@cleverbeagle/dates": "^0.5.1",
"@cleverbeagle/seeder": "^1.3.1",
+ "@turf/truncate": "^5.1.5",
"@turf/union": "^5.1.5",
"babel-runtime": "^6.26.0",
"bcrypt": "^1.0.3",
From 08b3289f4d58f71767f7e3a6903635cb1caa674c Mon Sep 17 00:00:00 2001
From: vjrj
Date: Mon, 12 Feb 2018 19:52:22 +0100
Subject: [PATCH 022/330] Lang for oauth users
---
.../api/Users/server/send-welcome-email.js | 3 +-
imports/modules/get-oauth-profile.js | 37 +++++++++----------
.../startup/server/accounts/lang-fallback.js | 8 ++++
.../startup/server/accounts/on-create-user.js | 13 +++++--
public/locales/en/common.json | 1 +
public/locales/es/common.json | 5 ++-
6 files changed, 42 insertions(+), 25 deletions(-)
create mode 100644 imports/startup/server/accounts/lang-fallback.js
diff --git a/imports/api/Users/server/send-welcome-email.js b/imports/api/Users/server/send-welcome-email.js
index 10748dd..f93c7d2 100644
--- a/imports/api/Users/server/send-welcome-email.js
+++ b/imports/api/Users/server/send-welcome-email.js
@@ -10,11 +10,12 @@ export default (options, user, lang) => {
const applicationName = i18n.t('AppName');
const firstName = OAuthProfile ? OAuthProfile.name.first : options.profile.name.first;
const emailAddress = OAuthProfile ? OAuthProfile.email : options.email;
+ const welcome = i18n.t('welcome');
if (emailAddress) {
sendEmail({
to: emailAddress,
from: `${applicationName} `,
- subject: `[${applicationName}] Welcome, ${firstName}!`,
+ subject: `[${applicationName}] ${welcome} ${firstName}!`,
lang,
template: 'welcome',
templateVars: {
diff --git a/imports/modules/get-oauth-profile.js b/imports/modules/get-oauth-profile.js
index 91866dc..8e427a1 100644
--- a/imports/modules/get-oauth-profile.js
+++ b/imports/modules/get-oauth-profile.js
@@ -1,12 +1,11 @@
-const parseGoogleData = service => {
- return {
- email: service.email,
- name: {
- first: service.given_name,
- last: service.family_name,
- },
- };
-};
+const parseGoogleData = service => ({
+ email: service.email,
+ name: {
+ first: service.given_name,
+ last: service.family_name
+ },
+ lang: service.locale
+});
const parseGithubData = (profile, service) => {
const name = profile.name.split(' ');
@@ -14,20 +13,18 @@ const parseGithubData = (profile, service) => {
email: service.email,
name: {
first: name[0],
- last: name[1],
- },
+ last: name[1]
+ }
};
};
-const parseFacebookData = service => {
- return {
- email: service.email,
- name: {
- first: service.first_name,
- last: service.last_name,
- },
- };
-};
+const parseFacebookData = service => ({
+ email: service.email,
+ name: {
+ first: service.first_name,
+ last: service.last_name
+ }
+});
const getDataForService = (profile, services) => {
if (services.facebook) return parseFacebookData(services.facebook);
diff --git a/imports/startup/server/accounts/lang-fallback.js b/imports/startup/server/accounts/lang-fallback.js
new file mode 100644
index 0000000..af96ce5
--- /dev/null
+++ b/imports/startup/server/accounts/lang-fallback.js
@@ -0,0 +1,8 @@
+const getFallbackLang = (lang) => {
+ if (lang && (lang === 'ast' || lang === 'gl' || lang === 'eu' || lang === 'ca' || lang.match(/^es/))) {
+ return 'es';
+ }
+ return 'en';
+};
+
+export default getFallbackLang;
diff --git a/imports/startup/server/accounts/on-create-user.js b/imports/startup/server/accounts/on-create-user.js
index fa2a99e..383b8c0 100644
--- a/imports/startup/server/accounts/on-create-user.js
+++ b/imports/startup/server/accounts/on-create-user.js
@@ -1,18 +1,25 @@
import { Accounts } from 'meteor/accounts-base';
import sendWelcomeEmail from '../../../api/Users/server/send-welcome-email';
+import getOAuthProfile from '../../../modules/get-oauth-profile';
+import getFallbackLang from './lang-fallback';
Accounts.onCreateUser((options, user) => {
const userToCreate = user;
// console.log(JSON.stringify(user));
// console.log(JSON.stringify(options));
+
let lang = 'en'; // fallback
if (options.profile) {
userToCreate.profile = options.profile;
userToCreate.lang = options.profile.lang;
- lang = options.profile.lang;
+ // console.log(JSON.stringify(user));
+ lang = userToCreate.lang;
delete options.profile.lang;
- } else {
- // TODO others (google, etc) ?
+ }
+ const OAuthProfile = getOAuthProfile(options, user);
+ if (OAuthProfile) {
+ userToCreate.lang = getFallbackLang(OAuthProfile && OAuthProfile.lang ? OAuthProfile.lang : lang);
+ lang = userToCreate.lang;
}
sendWelcomeEmail(options, userToCreate, lang);
return userToCreate;
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index 6b383de..1f1c564 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -158,6 +158,7 @@
"Más información sobre este fuego": "More information about this fire",
"termsAccept": "I accept the <1>conditions of service1> of this site",
"Bienvenid@!": "Welcome!",
+ "welcome": "Welcome",
"Verifica tu dirección de correo": "Verify Your Email Address",
"Zonas vigiladas": "Monitored areas",
"En verde, las zonas vigiladas por nuestros usuari@s actualmente": "In green, the areas monitored by our users currently",
diff --git a/public/locales/es/common.json b/public/locales/es/common.json
index 0231e6e..15763d8 100644
--- a/public/locales/es/common.json
+++ b/public/locales/es/common.json
@@ -228,6 +228,7 @@
"Última actualización, {{when}}": "Última actualización, {{when}}",
"termsAccept": "Acepto las <1>condiciones de servicio1> de este sitio",
"Bienvenid@!": "Bienvenid@!",
+ "welcome": "Bienvenid@",
"Has iniciado sesión con {{service}} usando la dirección de correo {{email}}.": "Has iniciado sesión con {{service}} usando la dirección de correo {{email}}.",
"Verifica tu dirección de correo":
"Verifica tu dirección de correo",
@@ -269,5 +270,7 @@
"Detectado":
"Detectado",
"Zonas vigiladas por nuestros usuari@s actualmente":
- "Zonas vigiladas por nuestros usuari@s actualmente"
+ "Zonas vigiladas por nuestros usuari@s actualmente",
+ "Iniciar sesión con Facebook":
+ "Iniciar sesión con Facebook"
}
From a7f9e51b5f0862a907d6d27ed26ca2ccd3ce0659 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Mon, 12 Feb 2018 19:56:46 +0100
Subject: [PATCH 023/330] Truncate subs union
---
imports/ui/components/Maps/SubsUnion/SubsUnion.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/imports/ui/components/Maps/SubsUnion/SubsUnion.js b/imports/ui/components/Maps/SubsUnion/SubsUnion.js
index e179b33..60cbd26 100644
--- a/imports/ui/components/Maps/SubsUnion/SubsUnion.js
+++ b/imports/ui/components/Maps/SubsUnion/SubsUnion.js
@@ -20,7 +20,7 @@ function unify(polyList) {
if (i === 0) {
unionTemp = cleanPol;
} else {
- unionTemp = tunion(unionTemp, cleanPol);
+ unionTemp = ttrunc(tunion(unionTemp, cleanPol), truncOptions);
}
}
return unionTemp;
From bc4201ffe679fd2ec4d260cc8d417d94503aeeb6 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Mon, 12 Feb 2018 20:00:45 +0100
Subject: [PATCH 024/330] Styling in error message on mobile
---
imports/ui/components/ErrorBoundary/ErrorBoundary.js | 4 ++--
imports/ui/components/ErrorBoundary/ErrorBoundary.scss | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/imports/ui/components/ErrorBoundary/ErrorBoundary.js b/imports/ui/components/ErrorBoundary/ErrorBoundary.js
index 542894f..cc1f95b 100644
--- a/imports/ui/components/ErrorBoundary/ErrorBoundary.js
+++ b/imports/ui/components/ErrorBoundary/ErrorBoundary.js
@@ -35,9 +35,9 @@ class ErrorBoundary extends Component {
{this.t('AppNameFull')}
{this.t('general-error-title')}
-
+
{this.t('general-error-description')}
-
+
diff --git a/imports/ui/components/ErrorBoundary/ErrorBoundary.scss b/imports/ui/components/ErrorBoundary/ErrorBoundary.scss
index 9ced77e..225e4ca 100644
--- a/imports/ui/components/ErrorBoundary/ErrorBoundary.scss
+++ b/imports/ui/components/ErrorBoundary/ErrorBoundary.scss
@@ -2,9 +2,9 @@
background-image: url('/error-background.png');
height: 100%;
min-height: 55vh;
- margin-top: 40px;
+ margin: 40px 10px 10px 10px;
}
.error-boundary > div > div > h4 {
- line-height: 1.5em;
+ line-height: 1.3em;
}
From 981e8e798e513d6cf07b2a35b14a4c9991446c1e Mon Sep 17 00:00:00 2001
From: vjrj
Date: Tue, 13 Feb 2018 01:44:15 +0100
Subject: [PATCH 025/330] FiresMap store checkbox and improved performance
---
imports/ui/pages/FiresMap/FiresMap.js | 80 +++++++++++++++++++++++----
1 file changed, 68 insertions(+), 12 deletions(-)
diff --git a/imports/ui/pages/FiresMap/FiresMap.js b/imports/ui/pages/FiresMap/FiresMap.js
index ffbf2f2..b75d26b 100644
--- a/imports/ui/pages/FiresMap/FiresMap.js
+++ b/imports/ui/pages/FiresMap/FiresMap.js
@@ -41,6 +41,8 @@ const MAXZOOMREACTIVE = 6;
const zoom = new ReactiveVar(8);
const center = new ReactiveVar([0, 0]);
const mapSize = new ReactiveVar();
+const marks = new ReactiveVar(false);
+const showUnion = new ReactiveVar(true);
// Remove map in subscription
class FiresMap extends React.Component {
@@ -51,9 +53,11 @@ class FiresMap extends React.Component {
center: props.center,
zoom: props.zoom
},
- useMarkers: false,
+ // init: true,
+ useMarkers: props.marks,
scaleAdded: false,
- showSubsUnion: true
+ moving: false,
+ showSubsUnion: props.showUnion
};
const self = this;
// viewportchange
@@ -62,12 +66,43 @@ class FiresMap extends React.Component {
self.handleViewportChange(viewport);
}, 1500);
this.onViewportChanged = this.onViewportChanged.bind(this);
+ this.onMoveEnd = this.onMoveEnd.bind(this);
+ this.onMoveStart = this.onMoveStart.bind(this);
}
componentDidMount() {
}
+ /* shouldComponentUpdate(nextProps, nextState) {
+ * const notMoving = !nextState.moving;
+ * const markersChanged = this.state.useMarkers !== nextState.useMarkers;
+ * const unionChanged = this.state.showSubsUnion !== nextState.showSubsUnion;
+ * const otherViewport = this.state.viewport !== nextState.viewport;
+ * // const init = nextState.viewport.center === [0, 0];
+ * // console.log(notMoving ? 'Not moving map' : 'Moving map');
+ * // console.log(otherViewport ? 'Other viewport' : 'Not other viewport');
+ * console.log(`${otherViewport ? 'OTHER' : 'Not other'} viewport ${nextState.viewport.center} zoom: ${nextState.viewport.zoom}`);
+ * return this.state.init || (notMoving && otherViewport && this.state.moved) || unionChanged || markersChanged;
+ * }
+ */
+
+ shouldComponentUpdate(nextProps, nextState) {
+ const notMoving = !nextState.moving;
+ return notMoving;
+ }
+
+ onMoveStart() {
+ // this.setState({ moving: true });
+ this.state.moving = true;
+ }
+
+ onMoveEnd() {
+ // this.setState({ moving: false });
+ this.state.moving = false;
+ }
+
onViewportChanged(viewport) {
+ this.debounceView.cancel();
this.debounceView(viewport);
}
@@ -77,6 +112,11 @@ class FiresMap extends React.Component {
setShowSubsUnion(showSubsUnion) {
this.setState({ showSubsUnion });
+ store.set('firesmap_showunion', showSubsUnion);
+ }
+
+ componentDidUnMount() {
+ // this.setState({ init: true });
}
handleViewportChange(viewport) {
@@ -94,7 +134,8 @@ class FiresMap extends React.Component {
}
zoom.set(viewport.zoom);
center.set(viewport.center);
- this.setState({ viewport });
+ // this.setState({ viewport });
+ this.state.viewport = viewport;
}
}
@@ -104,6 +145,7 @@ class FiresMap extends React.Component {
useMarkers(use) {
this.setState({ useMarkers: use });
+ store.set('firesmap_marks', use);
}
addScale(map) {
@@ -116,7 +158,7 @@ class FiresMap extends React.Component {
}
handleLeafletLoad(map) {
- if (map && map.leafletElement) {
+ if (map && map.leafletElement && !this.state.moving) {
const lmap = map.leafletElement;
try {
const bounds = lmap.getBounds();
@@ -179,7 +221,7 @@ class FiresMap extends React.Component {
Resaltar en verde el área vigilada por nuestros usuarios/as (*)
{(this.state.viewport.zoom >= MAXZOOM) &&
- this.useMarkers(e.target.checked)}>
+ this.useMarkers(e.target.checked)}>
Resaltar los fuegos con un marcador
}
}
@@ -201,13 +243,16 @@ class FiresMap extends React.Component {
className="firesmap-leaflet-container"
animate
minZoom={5}
- center={this.props.center}
- zoom={this.props.zoom}
+ center={this.state.viewport.center}
+ zoom={this.state.viewport.zoom}
preferCanvas
- onClick={this.onClickReset}
viewport={this.state.viewport}
onViewportChanged={this.onViewportChanged}
sleep={isHome() && !isChrome}
+ onMoveend={this.onMoveEnd}
+ onMovestart={this.onMoveStart}
+ onZoomend={this.onMoveEnd}
+ onZoomstart={this.onMoveStart}
sleepTime={10750}
wakeTime={750}
sleepNote
@@ -279,24 +324,33 @@ FiresMap.propTypes = {
activefirestotal: PropTypes.number.isRequired,
center: PropTypes.arrayOf(PropTypes.number),
zoom: PropTypes.number,
+ marks: PropTypes.bool.isRequired,
+ showUnion: PropTypes.bool.isRequired,
history: PropTypes.object.isRequired,
t: PropTypes.func.isRequired
};
-let init = true;
+let geoInit = true;
export default translate([], { wait: true })(withTracker(() => {
let subscription;
const centerStored = store.get('firesmap_center');
const zoomStored = store.get('firesmap_zoom');
+ const marksStored = store.get('firesmap_marks');
+ const showUnionStored = store.get('firesmap_showunion');
zoom.set(zoomStored || 8);
-
+ if (typeof marksStored === 'boolean') {
+ marks.set(marksStored);
+ }
+ if (typeof showUnionStored === 'boolean') {
+ showUnion.set(showUnionStored);
+ }
Meteor.autorun(() => {
- if ((centerStored || geolocation.get()) && init) {
+ if ((centerStored || geolocation.get()) && geoInit) {
center.set(centerStored || geolocation.get());
// console.log(`Geolocation ${geolocation.get()}`);
- init = false;
+ geoInit = false;
}
if (mapSize.get() && mapSize.get()[0].lng && mapSize.get()[1].lat) {
subscription = Meteor.subscribe(
@@ -342,6 +396,8 @@ export default translate([], { wait: true })(withTracker(() => {
falsePositives,
lastCheck: lastCheck ? lastCheck.value : null,
center: center.get(),
+ marks: marks.get(),
+ showUnion: showUnion.get(),
zoom: zoom.get()
};
})(FiresMap));
From dfed8d3a74f39065b474622efe147676c66f7415 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Tue, 13 Feb 2018 09:24:47 +0100
Subject: [PATCH 026/330] Nav reorder
---
imports/ui/components/Navigation/Navigation.js | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/imports/ui/components/Navigation/Navigation.js b/imports/ui/components/Navigation/Navigation.js
index f252702..e3e3937 100644
--- a/imports/ui/components/Navigation/Navigation.js
+++ b/imports/ui/components/Navigation/Navigation.js
@@ -47,12 +47,13 @@ const Navigation = props => (
{props.authenticated ? Mis zonas : Participar }
-
- {props.t('activeFires')}
-
- {props.t('Zonas vigiladas')}
+ {props.t('Zonas vigiladas')}
+
+ {props.t('activeFires')}
+
+
{!props.authenticated ? : }
{/* */}
From add5dedcb149af98caa4146f65c01e0598322a03 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Tue, 13 Feb 2018 09:24:56 +0100
Subject: [PATCH 027/330] Migrating users without lang
---
imports/startup/server/migrations.js | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/imports/startup/server/migrations.js b/imports/startup/server/migrations.js
index a4482f0..6002d4d 100644
--- a/imports/startup/server/migrations.js
+++ b/imports/startup/server/migrations.js
@@ -104,8 +104,21 @@ Meteor.startup(() => {
}
});
+ Migrations.add({
+ version: 7,
+ up: function defLangIfNull() {
+ Meteor.users.find({ lang: null }).forEach((user) => {
+ Meteor.users.update({ _id: user._id }, {
+ $set: {
+ lang: 'es'
+ }
+ });
+ });
+ }
+ });
+
// Set createdAt in users & subs
Migrations.migrateTo('latest');
- // Migrations.migrateTo('5,rerun');
+ // Migrations.migrateTo('7,rerun');
});
From 9e4815d8b80d466be63785176a667659c5d7ca26 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Tue, 13 Feb 2018 09:38:58 +0100
Subject: [PATCH 028/330] Link to tranlator
---
imports/ui/pages/Index/Index.js | 2 +-
public/locales/en/common.json | 2 +-
public/locales/es/common.json | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/imports/ui/pages/Index/Index.js b/imports/ui/pages/Index/Index.js
index 4cfa2f4..d353423 100644
--- a/imports/ui/pages/Index/Index.js
+++ b/imports/ui/pages/Index/Index.js
@@ -289,7 +289,7 @@ class Index extends Component {
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index 1f1c564..f27af55 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -105,7 +105,7 @@
"Otros dispositivos": "Other Devices",
"support-us-home": "We are developing our tools for other devices. You can <1>help make it possible.1>",
"Software Libre": "Free/Open/Libre Software",
- "dev-with-us-home": "All of our work is free/open software. <1>Translators and developers always welcome1>.",
+ "dev-with-us-home": "All of our work is <1>free/open software1>. <3>Translators3> and <5>developers always welcome5>.",
"Suscríbete a alertas de fuegos": "Subscribe to fire alerts",
"Gracias por Participar": "Thanks for participating",
"También puedes seguirnos en la web": "You can also follow us on the web",
diff --git a/public/locales/es/common.json b/public/locales/es/common.json
index 15763d8..38d75b8 100644
--- a/public/locales/es/common.json
+++ b/public/locales/es/common.json
@@ -165,7 +165,6 @@
"Otros dispositivos": "Otros dispositivos",
"support-us-home": "Estamos desarrollando nuestras herramientas para otros dispositivos. Puedes <1>contribuir a hacerlo posible.1>",
"Software Libre": "Software Libre",
- "dev-with-us-home": "Todo nuestro trabajo es sofware libre. <1>Traductoræs y desarrolladoræs siempre bienvenid@s1>.",
"Suscríbete a alertas de fuegos": "Suscríbete a alertas de fuegos",
"Gracias por Participar": "Gracias por Participar",
"También puedes seguirnos en la web": "También puedes seguirnos en la web",
@@ -174,6 +173,7 @@
"Sí": "Sí",
"No": "No",
"Sobre los datos y imágenes usados": "Sobre los datos y imágenes usados",
+ "dev-with-us-home": "Todo nuestro trabajo es <1>sofware libre1>. <3>Traductoræs3> y <5>desarrolladoræs siempre bienvenid@s5>.",
"Términos de Servicio": "Términos de Servicio",
"Política de Privacidad": "Política de Privacidad",
"No estás suscrito a fuegos en ninguna zona": "No estás suscrito a fuegos en ninguna zona",
From 26d668b68cbaaeaf94751b79d2e44094507e0e55 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Tue, 13 Feb 2018 09:39:23 +0100
Subject: [PATCH 029/330] Send missing in dev
---
imports/startup/client/i18n.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/imports/startup/client/i18n.js b/imports/startup/client/i18n.js
index 42c0ab3..63abf76 100644
--- a/imports/startup/client/i18n.js
+++ b/imports/startup/client/i18n.js
@@ -53,7 +53,7 @@ i18nOpts.react = {
nsMode: 'default' */
};
-const sendMissing = false; // Meteor.isDevelopment;
+const sendMissing = true; // Meteor.isDevelopment;
if (sendMissing && Meteor.isDevelopment) {
i18nOpts.sendMissing = true;
i18nOpts.missingKeyHandler = function miss(lng, ns, key, defaultValue) {
From 0bb1df35842e06bc746e814e3868e1c8c584cbb4 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Tue, 13 Feb 2018 08:49:25 +0000
Subject: [PATCH 030/330] Translated using Weblate (English)
Currently translated at 100.0% (187 of 187 strings)
---
public/locales/en/common.json | 365 +++++++++++++++++-----------------
1 file changed, 187 insertions(+), 178 deletions(-)
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index 1f1c564..d9e5177 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -1,180 +1,189 @@
{
- "AppName": "All Against Fire",
- "AppNameFull": "All Against Fire!",
- "AppDescrip": "Crowdsourcing against wildfires",
- "AppDescripLong": "We use different sources of data to notify you of active fires in your areas of interest",
- "OrgName": "Comunes",
- "OrgNameFull": "Comunes Association",
- "Términos": "Terms",
- "Términos de Servicio": "Terms of Service",
- "Privacidad": "Privacy",
- "Inicio": "Home",
- "Licencia": "License",
- "Cerrar sesión": "Logout",
- "Registrarse": "Sign Up",
- "Regístrate": "Sign Up",
- "Iniciar sesión": "Login",
- "o regístrate con un correo": "or Sign Up with an Email Address",
- "o con un correo": "or with an Email Address",
- "Nombre": "First Name",
- "Apellidos": "Last Name",
- "Correo electrónico": "Email Address",
- "Contraseña": "Password",
- "¿Ya tienes un cuenta?": "Already have an account?",
- "Usa al menos seis caracteres.": "Use at least six characters.",
- "Iniciar sesión con Google": "Log In with Google",
- "¿Olvidaste tu contraseña?": "Forgot password?",
- "¿No tienes una cuenta?": "Don't have an account?",
- "Introduce tu correo abajo para recibir un enlace para resetear tu contraseña.": "Enter your email address below to receive a link to reset your password.",
- "Recupera tu contraseña": "Recover Password",
- "¿Recuerdas tu contraseña?": "Remember your password?",
- "Necesitamos un correo aquí.": "Need an email address here.",
- "¿Es este correo correcto?": "Is this email address correct?",
- "Necesitamos una contraseña aquí.": "Need a password here.",
- "Bienvenid@ de nuevo": "Welcome back",
- "Guardar perfíl": "Save profile",
- "Contraseña actual": "Current Password",
- "Nueva contraseña": "New Password",
- "Editar perfíl": "Edit Profile",
- "Editar perfíl en": "Edit Profile on",
- "¡Perfíl actualizado!": "Profile updated!",
- "¿Cuál es tu nombre?": "What's your first name?",
- "¿Cuál es tu apellido?": "What's your lastName name?",
- "Necesito tu contraseña si la quieres cambiar.": "Need your current password if changing.",
- "Necesito tu nueva contraseña si la quieres cambiar.": "Need your new password if changing.",
- "¿Es correcto este correo?": "Is this email address correct?",
- "verifyEmail": "Hey friend! Can you verify your email address ({{email}}) for us?",
- "Reenviar email de verificación": "Re-send verification email",
- "checkVerificationEmail": "Check {{email}} for a verification link!",
- "checkResetEmail": "Check ${email} for a reset link!",
- "¡Listo, gracias!": "All set, thanks!",
- "Por favor, inténtalo otra vez.": "Please try again.",
- "Verificando...": "Verifying...",
- "Introduce una nueva contraseña, por favor.": "Enter a new password, please.",
- "Repite tu nueva contraseña, por favor.": "Repeat your new password, please.",
- "Mmmm, tus contraseñas no coinciden. Inténtalo otra vez": "Hmm, your passwords don't match. Try again",
- "Para resetear tu contraseña, introduce una nueva debajo. Iniciarás la sesión con la nueva contraseña.": "To reset your password, enter a new one below. You will be logged in with your new password.",
- "Resetea tu contraseña": "Reset your Password",
- "Repite la nueva contraseña": "Repeat New Password",
- "Resetea la contraseña y entra": "Reset Password & Login",
- "Imágenes capturadas por los satélites de la NASA muestran el humo de grandes incendios que se extienden sobre el Océano Pacífico. La actividad del fuego está delineada en rojo.": "Images captured by NASA satellites show the smoke of large fires spreading over the Pacific Ocean. Fire activity is outlined in red.",
- "Todavía sin subscriptiones": "No subscriptions yet",
- "Suscripción añadida": "Subscription added",
- "Suscripción actualizada": "Subscription updated",
- "Última actualización, {{when}}": "Last updated, {{when}}",
- "Has iniciado sesión con {{service}} usando la dirección de correo {{email}}.": "You're logged in with {{service}} using the email address {{email}}.",
- "Uso de Cookies": "Use of Cookies",
- "Utilizamos cookies para asegurar un mejor uso de nuestra web. Si continúas navegando, consideramos que aceptas su uso": "We use cookies to ensure a better use of our website. If you continue browsing, we consider that you accept their use",
- "Participa": "Get Involved",
- "activeFires": "Active Fires",
- "Fuegos activos": "Active Fires",
- "noActiveFireInMapCount": "There are no active fires in this area of the map. There is a total of <1><0>{{countTotal}}0>1> active fires detected worldwide.",
- "activeFireInMapCount": "On the map marked in red <1><0>{{count,number}}0>1> active fires. There is a total of <3><0>{{countTotal,number}} 0>3> active fires detected worldwide by NASA.",
- "activeNeigFireInMapCount": "In Orange, the fires recently reported by our users.",
- "Centrar en tu ubicación": "Center on your location",
- "Resaltar en verde el área vigilada por nuestros usuarios/as": "The area monitored by our users is highlighted in green",
- "Resaltar los fuegos con un marcador": "Highlight the fires with a marker",
- "mapPrivacy": "<0>In order to preserve the privacy of our users, the reflected data are randomly altered and are only indicative.0>",
- "Nuevas notificaciones de {{app}}": "New notifications of {{app}}",
- "Mis zonas": "My areas",
- "Nueva zona": "New area",
- "Subscribirme a fuegos en este rádio": "Subscribe to fires in this radio",
- "Créditos": "Credits",
- "Escribe aquí un lugar": "Write here a place",
- "Indícanos la posición de la zona a vigilar (por ej. tu pueblo, una calle, etc):": "Tell us the position of the area to be monitored (eg your town, a street, etc.):",
- "También puedes seleccionar la zona en el mapa arrastrando el puntero naranja.": "You can also select the area on the map by dragging the orange pointer.",
- "¿A que distancia a la redonda quieres recibir notificaciones?": "At what distance around you want to receive notifications?",
- "Pulsa para activar": "Click to activate",
- "Arrastrar para seleccionar otro punto": "Drag to select another point",
- "Mapa gris de OpenStreetMap": "Grey map from OpenStreetMap",
- "Mapa color de OpenStreetMap": "Color OpenStreetMap",
- "Mapa de carreteras de Google": "Google road map",
- "Mapa de terreno de Google": "Google terrain map",
- "Mapa de satélite de Google": "Google satellite map",
- "Zona añadida": "Zone added",
- "Añadir zona": "Add zone",
- "Editar": "Edit",
- "Suscripciones a alertas de fuegos en zonas de mi interés": "Subscriptions to alerts for fires in areas of my interest",
- "En verde, áreas de las que recibirás alertas de fuegos": "In green, areas from which you will receive fire alerts",
- "Terminar": "Finish",
- "Pulsa para borrar": "Click to delete",
- "Pulsa aquí para borrar la zona": "Click here to remove the area",
- "Los fuegos activos se actualizan en tiempo real.": "Fires are updated in real-time.",
- "Somos muchos ojos": "We are many eyes",
- "Usa nuestro bot de Telegram para estar al tanto de los fuegos en tus área": "Use our Telegram bot to be aware of the fires in your area",
- "Otros dispositivos": "Other Devices",
- "support-us-home": "We are developing our tools for other devices. You can <1>help make it possible.1>",
- "Software Libre": "Free/Open/Libre Software",
- "dev-with-us-home": "All of our work is free/open software. <1>Translators and developers always welcome1>.",
- "Suscríbete a alertas de fuegos": "Subscribe to fire alerts",
- "Gracias por Participar": "Thanks for participating",
- "También puedes seguirnos en la web": "You can also follow us on the web",
- "Participar": "Participate",
- "Dejarás de recibir notificaciones de fuegos en esa área ¿Estás seguro/a? ": "Are you sure you want to stop getting notifications of fires in this area? ",
- "Sí": "Yes",
- "No": "No",
- "Sobre los datos y imágenes usados": "About the data and images used",
- "Política de Privacidad": "Privacy Policy",
- "No estás suscrito a fuegos en ninguna zona": "You are not subscribed to fires in any area",
- "Iniciar sesión con Telegram": "Login with Telegram",
- "Idioma": "Language",
- "fireDetected": "fire detected {{when}}",
- "fireDetectedAt": "fire detected on {{when}}",
- "Comentarios": "Comments",
- "Guardar": "Save",
- "Responder": "Reply",
- "Borrar": "Delete",
- "Añadir un comentario": "Add a comment",
- "Añadir una respuesta": "Add a Reply",
- "Añadir comentario": "Add a comment",
- "Necesitas iniciar sesión para": "You need to login to",
- "añadir comentarios": "add comments",
- "puntuar comentarios": "rate it comments",
- "responder": "reply",
- "Más comentarios": "More Comments",
- "Elige un lugar": "Choose a place",
- "Elige un radio de vigilancia": "Choose a watch radio",
- "Recibe alertas de fuegos en esa zona": "Get alerts of fires in that area",
- "Alerta cuando hay un fuego": "Alert when there is a fire",
- "Anterior": "Previous",
- "Siguiente": "Next",
- "Siempre alerta a los fuegos en nuestro vecindario": "Always alert to the fires in our neighborhood",
- "Información adicional sobre fuego detectado en {{where}} el {{when}}": "Additional information about fire detected in {{where}} on {{when}}",
- "Información adicional sobre fuego detectado el {{when}}": "Additional information about fire detected on {{when}}",
- "Coordenadas:": "Coordinates:",
- "Fuego detectado por satélites de la NASA <1>1>": "Fire detected by NASA satellites <1>1>",
- "Puedes añadir un comentario si tienes información adicional sobre este fuego.": "You can add a comment if you have additional information about this fire.",
- "Por ejemplo:": "For example:",
- "si conoces esta zona y cómo acceder al fuego (esto puede de ser de ayuda para apagarlo si sigue activo o para investigarlo en un futuro)": "If you know the area and how to access the fire (this can be helpful to turn off if still active or to investigate in the future)",
- "si conoces el motivo por el que comenzó el fuego": "if you know the reason why the fire started",
- "si quieres denunciar algún tipo de ilegalidad, incluso anónimamente": "if you want to denounce some kind of illegality, even anonymously",
- "o cualquier otra información": "or any other information",
- "Haga zoom en una zona de su interés si quiere que los fuegos se actualicen en tiempo real.": "Zoom into an area of your interest if you want that fires to be updated in real time.",
- "Notificaciones": "Notifications",
- "Recibe nuestras notificaciones de fuegos por correo o en tu navegador": "Receive our fire notifications by mail or in your browser",
- "Fuego notificado por uno de nuestros usuarios/as <1>1>": "Fire reported by one of our users <1>1>",
- "No recibirás notificaciones de fuegos en este equipo, solo por correo": "You will not receive notifications of fires in this device, only by email",
- "not-found": "Oops: This page doesn't exist",
- "Más información sobre este fuego": "More information about this fire",
- "termsAccept": "I accept the <1>conditions of service1> of this site",
- "Bienvenid@!": "Welcome!",
- "welcome": "Welcome",
- "Verifica tu dirección de correo": "Verify Your Email Address",
- "Zonas vigiladas": "Monitored areas",
- "En verde, las zonas vigiladas por nuestros usuari@s actualmente": "In green, the areas monitored by our users currently",
- "Datos actualizados <1>1>.": "Data updated <1>1>.",
- "Información adicional sobre fuego": "Additional information about fire",
- "CO2emisions":
- "Did you know that wildfires <1>produce as much CO² as cars1> and <3>about ⅕ of all our carbon emissions3>?",
- "Ayúdanos a combatir el cambio climático y a proteger el medioambiente": "Help us fight climate change and protect the environment",
- "Fuego en La Tuna, Los Ángeles, Estados Unidos, septiembre de 2017":
- "La Tuna fire, LA, United States, September 2017",
- "Polución en la ciudad de Almaty, Kazakhstan, enero de 2014":
- "Smog over Almaty city, Kazakhstan, January 2014",
- "Fuente": "Source",
- "NASA": "NASA",
- "nuestros usuarios/as": "our users",
- "Pulsa para más información": "Click for more information",
- "Detectado":
- "Detected"
+ "AppName": "All Against Fire",
+ "AppNameFull": "All Against Fire!",
+ "AppDescrip": "Crowdsourcing against wildfires",
+ "AppDescripLong": "We use different sources of data to notify you of active fires in your areas of interest",
+ "OrgName": "Comunes",
+ "OrgNameFull": "Comunes Association",
+ "Términos": "Terms",
+ "Términos de Servicio": "Terms of Service",
+ "Privacidad": "Privacy",
+ "Inicio": "Home",
+ "Licencia": "License",
+ "Cerrar sesión": "Logout",
+ "Registrarse": "Sign Up",
+ "Regístrate": "Sign Up",
+ "Iniciar sesión": "Login",
+ "o regístrate con un correo": "or Sign Up with an Email Address",
+ "o con un correo": "or with an Email Address",
+ "Nombre": "First Name",
+ "Apellidos": "Last Name",
+ "Correo electrónico": "Email Address",
+ "Contraseña": "Password",
+ "¿Ya tienes un cuenta?": "Already have an account?",
+ "Usa al menos seis caracteres.": "Use at least six characters.",
+ "Iniciar sesión con Google": "Log In with Google",
+ "¿Olvidaste tu contraseña?": "Forgot password?",
+ "¿No tienes una cuenta?": "Don't have an account?",
+ "Introduce tu correo abajo para recibir un enlace para resetear tu contraseña.": "Enter your email address below to receive a link to reset your password.",
+ "Recupera tu contraseña": "Recover Password",
+ "¿Recuerdas tu contraseña?": "Remember your password?",
+ "Necesitamos un correo aquí.": "Need an email address here.",
+ "¿Es este correo correcto?": "Is this email address correct?",
+ "Necesitamos una contraseña aquí.": "Need a password here.",
+ "Bienvenid@ de nuevo": "Welcome back",
+ "Guardar perfíl": "Save profile",
+ "Contraseña actual": "Current Password",
+ "Nueva contraseña": "New Password",
+ "Editar perfíl": "Edit Profile",
+ "Editar perfíl en": "Edit Profile on",
+ "¡Perfíl actualizado!": "Profile updated!",
+ "¿Cuál es tu nombre?": "What's your first name?",
+ "¿Cuál es tu apellido?": "What's your lastName name?",
+ "Necesito tu contraseña si la quieres cambiar.": "Need your current password if changing.",
+ "Necesito tu nueva contraseña si la quieres cambiar.": "Need your new password if changing.",
+ "¿Es correcto este correo?": "Is this email address correct?",
+ "verifyEmail": "Hey friend! Can you verify your email address ({{email}}) for us?",
+ "Reenviar email de verificación": "Re-send verification email",
+ "checkVerificationEmail": "Check {{email}} for a verification link!",
+ "checkResetEmail": "Check ${email} for a reset link!",
+ "¡Listo, gracias!": "All set, thanks!",
+ "Por favor, inténtalo otra vez.": "Please try again.",
+ "Verificando...": "Verifying...",
+ "Introduce una nueva contraseña, por favor.": "Enter a new password, please.",
+ "Repite tu nueva contraseña, por favor.": "Repeat your new password, please.",
+ "Mmmm, tus contraseñas no coinciden. Inténtalo otra vez": "Hmm, your passwords don't match. Try again",
+ "Para resetear tu contraseña, introduce una nueva debajo. Iniciarás la sesión con la nueva contraseña.": "To reset your password, enter a new one below. You will be logged in with your new password.",
+ "Resetea tu contraseña": "Reset your Password",
+ "Repite la nueva contraseña": "Repeat New Password",
+ "Resetea la contraseña y entra": "Reset Password & Login",
+ "Imágenes capturadas por los satélites de la NASA muestran el humo de grandes incendios que se extienden sobre el Océano Pacífico. La actividad del fuego está delineada en rojo.": "Images captured by NASA satellites show the smoke of large fires spreading over the Pacific Ocean. Fire activity is outlined in red.",
+ "Todavía sin subscriptiones": "No subscriptions yet",
+ "Suscripción añadida": "Subscription added",
+ "Suscripción actualizada": "Subscription updated",
+ "Última actualización, {{when}}": "Last updated, {{when}}",
+ "Has iniciado sesión con {{service}} usando la dirección de correo {{email}}.": "You're logged in with {{service}} using the email address {{email}}.",
+ "Uso de Cookies": "Use of Cookies",
+ "Utilizamos cookies para asegurar un mejor uso de nuestra web. Si continúas navegando, consideramos que aceptas su uso": "We use cookies to ensure a better use of our website. If you continue browsing, we consider that you accept their use",
+ "Participa": "Get Involved",
+ "activeFires": "Active Fires",
+ "Fuegos activos": "Active Fires",
+ "noActiveFireInMapCount": "There are no active fires in this area of the map. There is a total of <1><0>{{countTotal}}0>1> active fires detected worldwide.",
+ "activeFireInMapCount": "On the map marked in red <1><0>{{count,number}}0>1> active fires. There is a total of <3><0>{{countTotal,number}} 0>3> active fires detected worldwide by NASA.",
+ "activeNeigFireInMapCount": "In Orange, the fires recently reported by our users.",
+ "Centrar en tu ubicación": "Center on your location",
+ "Resaltar en verde el área vigilada por nuestros usuarios/as": "The area monitored by our users is highlighted in green",
+ "Resaltar los fuegos con un marcador": "Highlight the fires with a marker",
+ "mapPrivacy": "<0>In order to preserve the privacy of our users, the reflected data are randomly altered and are only indicative.0>",
+ "Nuevas notificaciones de {{app}}": "New notifications of {{app}}",
+ "Mis zonas": "My areas",
+ "Nueva zona": "New area",
+ "Subscribirme a fuegos en este rádio": "Subscribe to fires in this radio",
+ "Créditos": "Credits",
+ "Escribe aquí un lugar": "Write here a place",
+ "Indícanos la posición de la zona a vigilar (por ej. tu pueblo, una calle, etc):": "Tell us the position of the area to be monitored (eg your town, a street, etc.):",
+ "También puedes seleccionar la zona en el mapa arrastrando el puntero naranja.": "You can also select the area on the map by dragging the orange pointer.",
+ "¿A que distancia a la redonda quieres recibir notificaciones?": "At what distance around you want to receive notifications?",
+ "Pulsa para activar": "Click to activate",
+ "Arrastrar para seleccionar otro punto": "Drag to select another point",
+ "Mapa gris de OpenStreetMap": "Grey map from OpenStreetMap",
+ "Mapa color de OpenStreetMap": "Color OpenStreetMap",
+ "Mapa de carreteras de Google": "Google road map",
+ "Mapa de terreno de Google": "Google terrain map",
+ "Mapa de satélite de Google": "Google satellite map",
+ "Zona añadida": "Zone added",
+ "Añadir zona": "Add zone",
+ "Editar": "Edit",
+ "Suscripciones a alertas de fuegos en zonas de mi interés": "Subscriptions to alerts for fires in areas of my interest",
+ "En verde, áreas de las que recibirás alertas de fuegos": "In green, areas from which you will receive fire alerts",
+ "Terminar": "Finish",
+ "Pulsa para borrar": "Click to delete",
+ "Pulsa aquí para borrar la zona": "Click here to remove the area",
+ "Los fuegos activos se actualizan en tiempo real.": "Fires are updated in real-time.",
+ "Somos muchos ojos": "We are many eyes",
+ "Usa nuestro bot de Telegram para estar al tanto de los fuegos en tus área": "Use our Telegram bot to be aware of the fires in your area",
+ "Otros dispositivos": "Other Devices",
+ "support-us-home": "We are developing our tools for other devices. You can <1>help make it possible.1>",
+ "Software Libre": "Free/Open/Libre Software",
+ "dev-with-us-home": "All of our work is free/open software. <1>Translators and developers always welcome1>.",
+ "Suscríbete a alertas de fuegos": "Subscribe to fire alerts",
+ "Gracias por Participar": "Thanks for participating",
+ "También puedes seguirnos en la web": "You can also follow us on the web",
+ "Participar": "Participate",
+ "Dejarás de recibir notificaciones de fuegos en esa área ¿Estás seguro/a? ": "Are you sure you want to stop getting notifications of fires in this area? ",
+ "Sí": "Yes",
+ "No": "No",
+ "Sobre los datos y imágenes usados": "About the data and images used",
+ "Política de Privacidad": "Privacy Policy",
+ "No estás suscrito a fuegos en ninguna zona": "You are not subscribed to fires in any area",
+ "Iniciar sesión con Telegram": "Login with Telegram",
+ "Idioma": "Language",
+ "fireDetected": "fire detected {{when}}",
+ "fireDetectedAt": "fire detected on {{when}}",
+ "Comentarios": "Comments",
+ "Guardar": "Save",
+ "Responder": "Reply",
+ "Borrar": "Delete",
+ "Añadir un comentario": "Add a comment",
+ "Añadir una respuesta": "Add a Reply",
+ "Añadir comentario": "Add a comment",
+ "Necesitas iniciar sesión para": "You need to login to",
+ "añadir comentarios": "add comments",
+ "puntuar comentarios": "rate it comments",
+ "responder": "reply",
+ "Más comentarios": "More Comments",
+ "Elige un lugar": "Choose a place",
+ "Elige un radio de vigilancia": "Choose a watch radio",
+ "Recibe alertas de fuegos en esa zona": "Get alerts of fires in that area",
+ "Alerta cuando hay un fuego": "Alert when there is a fire",
+ "Anterior": "Previous",
+ "Siguiente": "Next",
+ "Siempre alerta a los fuegos en nuestro vecindario": "Always alert to the fires in our neighborhood",
+ "Información adicional sobre fuego detectado en {{where}} el {{when}}": "Additional information about fire detected in {{where}} on {{when}}",
+ "Información adicional sobre fuego detectado el {{when}}": "Additional information about fire detected on {{when}}",
+ "Coordenadas:": "Coordinates:",
+ "Fuego detectado por satélites de la NASA <1>1>": "Fire detected by NASA satellites <1>1>",
+ "Puedes añadir un comentario si tienes información adicional sobre este fuego.": "You can add a comment if you have additional information about this fire.",
+ "Por ejemplo:": "For example:",
+ "si conoces esta zona y cómo acceder al fuego (esto puede de ser de ayuda para apagarlo si sigue activo o para investigarlo en un futuro)": "If you know the area and how to access the fire (this can be helpful to turn off if still active or to investigate in the future)",
+ "si conoces el motivo por el que comenzó el fuego": "if you know the reason why the fire started",
+ "si quieres denunciar algún tipo de ilegalidad, incluso anónimamente": "if you want to denounce some kind of illegality, even anonymously",
+ "o cualquier otra información": "or any other information",
+ "Haga zoom en una zona de su interés si quiere que los fuegos se actualicen en tiempo real.": "Zoom into an area of your interest if you want that fires to be updated in real time.",
+ "Notificaciones": "Notifications",
+ "Recibe nuestras notificaciones de fuegos por correo o en tu navegador": "Receive our fire notifications by mail or in your browser",
+ "Fuego notificado por uno de nuestros usuarios/as <1>1>": "Fire reported by one of our users <1>1>",
+ "No recibirás notificaciones de fuegos en este equipo, solo por correo": "You will not receive notifications of fires in this device, only by email",
+ "not-found": "Oops: This page doesn't exist",
+ "Más información sobre este fuego": "More information about this fire",
+ "termsAccept": "I accept the <1>conditions of service1> of this site",
+ "Bienvenid@!": "Welcome!",
+ "welcome": "Welcome",
+ "Verifica tu dirección de correo": "Verify Your Email Address",
+ "Zonas vigiladas": "Monitored areas",
+ "En verde, las zonas vigiladas por nuestros usuari@s actualmente": "In green, the areas monitored by our users currently",
+ "Datos actualizados <1>1>.": "Data updated <1>1>.",
+ "Información adicional sobre fuego": "Additional information about fire",
+ "CO2emisions": "Did you know that wildfires <1>produce as much CO² as cars1> and <3>about ⅕ of all our carbon emissions3>?",
+ "Ayúdanos a combatir el cambio climático y a proteger el medioambiente": "Help us fight climate change and protect the environment",
+ "Fuego en La Tuna, Los Ángeles, Estados Unidos, septiembre de 2017": "La Tuna fire, LA, United States, September 2017",
+ "Polución en la ciudad de Almaty, Kazakhstan, enero de 2014": "Smog over Almaty city, Kazakhstan, January 2014",
+ "Fuente": "Source",
+ "NASA": "NASA",
+ "nuestros usuarios/as": "our users",
+ "Pulsa para más información": "Click for more information",
+ "Detectado": "Detected",
+ "general-error-title": "Upppps: Something has gone wrong",
+ "general-error-description": "We are investigating the problem, try again in a while",
+ "Tomamos nota, ¡gracias por colaborar!": "We take note, thanks for collaborating!",
+ "Indícanos de que tipo de fuego se trata y ayúdanos así a mejorar nuestras notificaciones:": "Tell us what kind of fire it is and help us improve our notifications:",
+ "¿No es un fuego forestal?": "Isn't that a forest fire?",
+ "Regístrate o inicia sesión para aportar información sobre este fuego": "Sign up or login to provide information about this fire",
+ "Fuego no encontrado": "Fire not found",
+ "Es una industria": "It's an industry",
+ "Es una quema controlada": "It's a controlled burning",
+ "Parece una falsa alarma": "It seems a false alarm",
+ "Fuegos activos en el mundo actualizados en tiempo real": "Active fires in the world updated in real time",
+ "Zonas vigiladas por nuestros usuari@s actualmente": "Areas currently monitored by our users",
+ "Iniciar sesión con Facebook": "Login With Facebook"
}
From 3fc0fca23bf938a33f643a4a8b56074ca56fa848 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Tue, 13 Feb 2018 10:43:41 +0100
Subject: [PATCH 031/330] Added untranslated message
---
imports/ui/pages/Fires/Fires.js | 2 +-
public/locales/en/common.json | 4 +++-
public/locales/es/common.json | 4 +++-
3 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/imports/ui/pages/Fires/Fires.js b/imports/ui/pages/Fires/Fires.js
index 7b7f425..fe423a5 100644
--- a/imports/ui/pages/Fires/Fires.js
+++ b/imports/ui/pages/Fires/Fires.js
@@ -125,7 +125,7 @@ class Fire extends React.Component {
- {'Elige un tipo'}
+ {t('Elige un tipo')}
{Object.keys(FalsePositiveTypes).map(key => (
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index c46033e..dffcc24 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -185,5 +185,7 @@
"Parece una falsa alarma": "It seems a false alarm",
"Fuegos activos en el mundo actualizados en tiempo real": "Active fires in the world updated in real time",
"Zonas vigiladas por nuestros usuari@s actualmente": "Areas currently monitored by our users",
- "Iniciar sesión con Facebook": "Login With Facebook"
+ "Iniciar sesión con Facebook": "Login With Facebook",
+ "Elige un tipo":
+ "Choose a type"
}
diff --git a/public/locales/es/common.json b/public/locales/es/common.json
index 38d75b8..d4304e7 100644
--- a/public/locales/es/common.json
+++ b/public/locales/es/common.json
@@ -272,5 +272,7 @@
"Zonas vigiladas por nuestros usuari@s actualmente":
"Zonas vigiladas por nuestros usuari@s actualmente",
"Iniciar sesión con Facebook":
- "Iniciar sesión con Facebook"
+ "Iniciar sesión con Facebook",
+ "Elige un tipo":
+ "Elige un tipo"
}
From b5e9e301b93d0da7f6f4af97c05e0448268f22d7 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Wed, 14 Feb 2018 09:57:06 +0100
Subject: [PATCH 032/330] Test docs
---
README.md | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/README.md b/README.md
index 29a6a62..56a40d5 100644
--- a/README.md
+++ b/README.md
@@ -21,6 +21,15 @@ More platforms and services in the future...
We do tests via:
```
TEST_WATCH=1 MONGO_URL=mongodb://localhost:27017/fuegos meteor --settings settings-development.json test --driver-package meteortesting:mocha --port 3010
+
+# and
+
+chimp --watch --ddp=http://localhost:3000 --path=cucumber
+
+# and
+
+chimp --ddp=http://localhost:3000 --path=cucumber
+
```
## Data source acknowledgements
From fc4155ea49efc8eac97b9dd7da2fdeefbee8600a Mon Sep 17 00:00:00 2001
From: vjrj
Date: Wed, 14 Feb 2018 09:57:37 +0100
Subject: [PATCH 033/330] Prevent QT error in phantom
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 68b572a..3fd1c7b 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "tcef",
"private": true,
"scripts": {
- "start": "MONGO_URL=mongodb://localhost:27017/fuegos meteor --settings settings-development.json",
+ "start": "QT_QPA_PLATFORM='' MONGO_URL=mongodb://localhost:27017/fuegos meteor --settings settings-development.json",
"test": "jest"
},
"dependencies": {
From 8809a3e3c8351d47b3fa7437322360a2471cf092 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Wed, 14 Feb 2018 10:26:22 +0100
Subject: [PATCH 034/330] Some work with spiderable
---
cucumber/features/general.feature | 1 -
cucumber/features/pages.feature | 2 ++
cucumber/features/steps_definitions/pages.js | 9 ++++++
imports/ui/components/History/History.js | 7 -----
imports/ui/layouts/App/App.js | 32 +++++++++++++++++++-
5 files changed, 42 insertions(+), 9 deletions(-)
diff --git a/cucumber/features/general.feature b/cucumber/features/general.feature
index c3ea6a8..3cf078d 100644
--- a/cucumber/features/general.feature
+++ b/cucumber/features/general.feature
@@ -11,4 +11,3 @@ Feature: This app should generate a correct sitemap, etc
| fires | Active Fires |
| login | Login |
| signup | Signup |
- # And should be spiderables
diff --git a/cucumber/features/pages.feature b/cucumber/features/pages.feature
index 9814434..ed3f4fe 100644
--- a/cucumber/features/pages.feature
+++ b/cucumber/features/pages.feature
@@ -9,6 +9,7 @@ Feature: Test all secundary pages
| activeFires | Active Fires |
Then I check that all pages works properly
+ @watch
Scenario: Check that all secondary pages work well
Given a list of page urls and contents
| login | Login | true |
@@ -20,6 +21,7 @@ Feature: Test all secundary pages
| fire/inexistent | This page doesn't exist | false |
| fire/Fe26.2**1a0361ed0384f741403682e26b4bbc3850bee24e775da13c9782b22365ea895f*r_3gmVad5vzkeyqPpo6UcA*1s5fFz3iDGKTYP2RCvXMshof00QCHf4ErDl9K2dxoX0u-J-t6scyOWG8pGp3ehg_FfEtyR_kYcEKU3rE0jaSlZbD09TIvhiIJeS3C6Uc8YD-rit0XBrgsVKfYSxKzTRoOYiYFJ8JYd298hMtfiASePjS05Z58hhicyCcJYYRlarqDScG3LiVY3lL5y2nfcdIMNuSjCiKOJWuMkxwd9nR1UHMudLl0hEoy56mPdnHpDYtP9IYUlIOk1LlWBxcmHKifbXeqHu94p8j13Kk20dh2R49Hw3KsSoE9UbWmGQA9wAZXT82301i3rGF5GPAKjlTlRYcWisQurnPwHSVmx3DhUdiYwKGxt4KeaM5QVI4BE9octvE41OOprB_-Il105diQEh2Y9vdvX51ZVWIRfCboICPM6rJb0Oin7U7F1iM-oD_5s3DGnelfM5LGBcKwiB5paMo5M5vdBMaO-zR216cW9yGVXw9IZqHx8xDQWnoHAZjt8NLHeiGF2QOmIGtEUH7qnwhGpkcvszajmAZzR8saZgoH1qfBfvpVA41YfV14gU**4d030f05e23ad75409cebc609107467fc60be5077d52cf041087cd024fc4dc45*GY97aGFc1MyAsoO3Qxqtgwk9j-MbAPdEGBmEHq6r8VU | Additional information | false |
Then I check that all page urls works properly
+ # And they are spiderable
Scenario: Check that other non visible pages work well
Given a list of non visible pages ids and contents
diff --git a/cucumber/features/steps_definitions/pages.js b/cucumber/features/steps_definitions/pages.js
index 1ce3c8c..5074d52 100644
--- a/cucumber/features/steps_definitions/pages.js
+++ b/cucumber/features/steps_definitions/pages.js
@@ -71,4 +71,13 @@ module.exports = function () {
}
callback();
});
+
+ this.Then(/^they are spiderable$/, (callback) => {
+ // Write code here that turns the phrase above into concrete actions
+ for (let i = 0; i < pages.length; i += 1) {
+ client.url(`${process.env.ROOT_URL}/${pages[i][0]}?_escaped_fragment_=`);
+ client.waitForText('#react-root', pages[i][1]);
+ }
+ callback();
+ });
};
diff --git a/imports/ui/components/History/History.js b/imports/ui/components/History/History.js
index f72523c..a33464f 100644
--- a/imports/ui/components/History/History.js
+++ b/imports/ui/components/History/History.js
@@ -1,12 +1,5 @@
-import { Meteor } from 'meteor/meteor';
import createHistory from 'history/createBrowserHistory';
const history = createHistory();
-history.listen((location) => { // , action ) => {
- // console.log(location.pathname);
- // console.log(action); // PUSH, etc
- Meteor.Piwik.trackPage(location.pathname);
-});
-
export default history;
diff --git a/imports/ui/layouts/App/App.js b/imports/ui/layouts/App/App.js
index 9a11c69..f4cfc36 100644
--- a/imports/ui/layouts/App/App.js
+++ b/imports/ui/layouts/App/App.js
@@ -1,7 +1,7 @@
/* eslint-disable jsx-a11y/no-href */
/* eslint import/no-absolute-path: [2, { esmodule: false, commonjs: false, amd: false }] */
-import React from 'react';
+import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Router, Switch, Route } from 'react-router-dom';
import { Grid } from 'react-bootstrap';
@@ -54,11 +54,40 @@ import history from '../../components/History/History';
import '../../components/NotificationsObserver/NotificationsObserver';
import './App.scss';
+class LocationListener extends Component {
+ // https://stackoverflow.com/questions/43512450/react-router-v4-route-onchange-event
+ static contextTypes = {
+ router: PropTypes.object
+ };
+
+ componentDidMount() {
+ this.handleLocationChange(this.context.router.history.location);
+ this.unlisten =
+ this.context.router.history.listen(this.handleLocationChange);
+ }
+
+ componentWillUnmount() {
+ this.unlisten();
+ }
+
+ handleLocationChange(location) {
+ // your staff here
+ console.log(`----- location: '${location.pathname}'`);
+ Meteor.Piwik.trackPage(location.pathname);
+ // Meteor.isReadyForSpiderable = true;
+ }
+
+ render() {
+ return this.props.children;
+ }
+}
+
const App = props => (
/* https://react.i18next.com/components/i18nextprovider.html */
+
{ !props.loading ?
@@ -112,6 +141,7 @@ const App = props => (
}
: ''}
+
From 078931b4f94985eb99a71d7516bbe993e7362578 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Wed, 14 Feb 2018 10:31:19 +0100
Subject: [PATCH 035/330] Translation link
---
imports/ui/pages/Profile/Profile.js | 1 +
public/locales/en/common.json | 4 +++-
public/locales/es/common.json | 4 +++-
3 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/imports/ui/pages/Profile/Profile.js b/imports/ui/pages/Profile/Profile.js
index 4800200..3f3dbb2 100644
--- a/imports/ui/pages/Profile/Profile.js
+++ b/imports/ui/pages/Profile/Profile.js
@@ -214,6 +214,7 @@ class Profile extends React.Component {
}
+ {this.t('Puedes participar en las traducciones')}
{this.t('Contraseña actual')}
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index dffcc24..7c2740b 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -187,5 +187,7 @@
"Zonas vigiladas por nuestros usuari@s actualmente": "Areas currently monitored by our users",
"Iniciar sesión con Facebook": "Login With Facebook",
"Elige un tipo":
- "Choose a type"
+ "Choose a type",
+ "Puedes participar en las traducciones":
+ "You can help with the translations"
}
diff --git a/public/locales/es/common.json b/public/locales/es/common.json
index d4304e7..5d1a46d 100644
--- a/public/locales/es/common.json
+++ b/public/locales/es/common.json
@@ -274,5 +274,7 @@
"Iniciar sesión con Facebook":
"Iniciar sesión con Facebook",
"Elige un tipo":
- "Elige un tipo"
+ "Elige un tipo",
+ "Puedes participar en las traducciones":
+ "Puedes participar en las traducciones"
}
From 9ebbe5482a62908d001cfe084dc868f1d4ab5e14 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Wed, 14 Feb 2018 11:22:11 +0100
Subject: [PATCH 036/330] SiteSettings isPublic
---
imports/api/SiteSettings/SiteSettings.js | 1 +
imports/api/SiteSettings/server/publications.js | 2 +-
imports/startup/server/migrations.js | 10 ++++++++++
3 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/imports/api/SiteSettings/SiteSettings.js b/imports/api/SiteSettings/SiteSettings.js
index e690671..2bbd95f 100644
--- a/imports/api/SiteSettings/SiteSettings.js
+++ b/imports/api/SiteSettings/SiteSettings.js
@@ -40,6 +40,7 @@ SiteSettings.getSchema = type => new SimpleSchema({
type: String,
description: String,
value: SiteSettingsTypes[type].value,
+ isPublic: Boolean,
createdAt: defaultCreatedAt,
updatedAt: defaultUpdateAt
});
diff --git a/imports/api/SiteSettings/server/publications.js b/imports/api/SiteSettings/server/publications.js
index cd7f33d..ab9f1b2 100644
--- a/imports/api/SiteSettings/server/publications.js
+++ b/imports/api/SiteSettings/server/publications.js
@@ -3,4 +3,4 @@
import { Meteor } from 'meteor/meteor';
import SiteSettings from '../SiteSettings';
-Meteor.publish('settings', () => SiteSettings.find());
+Meteor.publish('settings', () => SiteSettings.find({ isPublic: true }));
diff --git a/imports/startup/server/migrations.js b/imports/startup/server/migrations.js
index 6002d4d..e58605f 100644
--- a/imports/startup/server/migrations.js
+++ b/imports/startup/server/migrations.js
@@ -117,6 +117,16 @@ Meteor.startup(() => {
}
});
+ Migrations.add({
+ version: 8,
+ up: function siteSettingsAddIndex() {
+ SiteSettings._ensureIndex({ isPublic: 1 }, { unique: 1 });
+ SiteSettings.find({ isPublic: null }).forEach((setting) => {
+ SiteSettings.update({ _id: setting._id }, { $set: { isPublic: true } });
+ });
+ }
+ });
+
// Set createdAt in users & subs
Migrations.migrateTo('latest');
From ed7c89476fd6f63f070bc6fbda9099d36e8b98d1 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Wed, 14 Feb 2018 11:26:14 +0100
Subject: [PATCH 037/330] Disable sitesettings methods
---
imports/api/SiteSettings/methods.js | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/imports/api/SiteSettings/methods.js b/imports/api/SiteSettings/methods.js
index 737bc3a..5f9d73d 100644
--- a/imports/api/SiteSettings/methods.js
+++ b/imports/api/SiteSettings/methods.js
@@ -1,11 +1,11 @@
import { Meteor } from 'meteor/meteor';
-import { check } from 'meteor/check';
+/* import { check } from 'meteor/check';
import SiteSettings from './SiteSettings';
-import SiteSettingsTypes from './SiteSettingsTypes';
+import SiteSettingsTypes from './SiteSettingsTypes'; */
import rateLimit from '../../modules/rate-limit';
Meteor.methods({
- 'siteSettings.insert': function siteSettingsInsert(setting) {
+/* 'siteSettings.insert': function siteSettingsInsert(setting) {
check(setting, {
name: String,
type: String,
@@ -46,14 +46,14 @@ Meteor.methods({
} catch (exception) {
throw new Meteor.Error('500', exception);
}
- }
+ } */
});
rateLimit({
methods: [
- 'siteSettings.insert',
+ /* 'siteSettings.insert',
'siteSettings.update',
- 'siteSettings.remove'
+ 'siteSettings.remove' */
],
limit: 5,
timeRange: 1000
From ac4331edbd8477cebfb728353043f8380fc480c3 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Wed, 14 Feb 2018 18:21:50 +0100
Subject: [PATCH 038/330] Subs union to server side
---
.../api/Subscriptions/server/publications.js | 35 ----
imports/startup/server/index.js | 1 +
imports/startup/server/migrations.js | 4 +-
imports/startup/server/subsUnion.js | 84 ++++++++++
.../ui/components/Maps/SubsUnion/SubsUnion.js | 92 +++-------
imports/ui/components/Maps/SubsUnion/Unify.js | 48 ++++++
imports/ui/pages/FiresMap/FiresMap.js | 20 ++-
.../pages/Subscriptions/SubscriptionsMap.js | 18 +-
package-lock.json | 158 +++++++++++++-----
package.json | 1 +
10 files changed, 299 insertions(+), 162 deletions(-)
create mode 100644 imports/startup/server/subsUnion.js
create mode 100644 imports/ui/components/Maps/SubsUnion/Unify.js
diff --git a/imports/api/Subscriptions/server/publications.js b/imports/api/Subscriptions/server/publications.js
index f67fc3a..adf6e13 100644
--- a/imports/api/Subscriptions/server/publications.js
+++ b/imports/api/Subscriptions/server/publications.js
@@ -3,43 +3,8 @@
import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import { check } from 'meteor/check';
-import Perlin from 'loms.perlin';
import Subscriptions from '../Subscriptions';
-Perlin.seed(Math.random());
-
-Meteor.publishTransformed('userSubsToFires', function transform() {
- // https://en.wikipedia.org/wiki/Location_obfuscation
- // https://en.wikipedia.org/wiki/Decimal_degrees#Precision
- // https://gis.stackexchange.com/questions/27792/what-simple-effective-techniques-for-obfuscating-points-are-available
- return Subscriptions.find().serverTransform(function transformDoc(odoc) {
- const doc = odoc;
- // Destructuring gives me an error: "Cannot destructure property `location` of 'undefined'"
- const location = doc.location;
- /* doc.lat = location.lat;
- * doc.lon = location.lon; */
- let lat;
- let lon;
- if (location) {
- lat = Math.round(location.lat * 10) / 10;
- lon = Math.round(location.lon * 10) / 10;
- // console.log(`[${lat}, ${lon}]`);
- const noiseBase = Perlin.perlin2(lat, lon);
- const noise = Math.abs(noiseBase / 3);
- // console.log(`Noise ${noise}, abs: ${Math.abs(noise)}`);
- lat += noise;
- lon += noise;
- doc.location.lat = lat;
- doc.location.lon = lon;
- doc.distance += noiseBase;
- }
- // console.log(`with noise: [${doc.lat}, ${doc.lon}]`);
- delete doc.chatId;
- delete doc.geo;
- return doc;
- });
-});
-
Meteor.publish('mysubscriptions', function subscriptions() {
return Subscriptions.find({ owner: this.userId }); // type: 'web'
});
diff --git a/imports/startup/server/index.js b/imports/startup/server/index.js
index aa32e9d..b331b73 100644
--- a/imports/startup/server/index.js
+++ b/imports/startup/server/index.js
@@ -9,3 +9,4 @@ import './notificationsObserver';
import './facts';
import '../common/comments';
import './sitemaps';
+import './subsUnion';
diff --git a/imports/startup/server/migrations.js b/imports/startup/server/migrations.js
index e58605f..f420f82 100644
--- a/imports/startup/server/migrations.js
+++ b/imports/startup/server/migrations.js
@@ -13,7 +13,7 @@ Meteor.startup(() => {
Migrations.config({
// Log job run details to console
- log: true
+ log: Meteor.isProduction
});
Migrations.add({
@@ -120,7 +120,7 @@ Meteor.startup(() => {
Migrations.add({
version: 8,
up: function siteSettingsAddIndex() {
- SiteSettings._ensureIndex({ isPublic: 1 }, { unique: 1 });
+ SiteSettings._ensureIndex({ isPublic: 1 });
SiteSettings.find({ isPublic: null }).forEach((setting) => {
SiteSettings.update({ _id: setting._id }, { $set: { isPublic: true } });
});
diff --git a/imports/startup/server/subsUnion.js b/imports/startup/server/subsUnion.js
new file mode 100644
index 0000000..d527f50
--- /dev/null
+++ b/imports/startup/server/subsUnion.js
@@ -0,0 +1,84 @@
+/* eslint-disable import/no-absolute-path */
+
+import { Meteor } from 'meteor/meteor';
+import Subscriptions from '/imports/api/Subscriptions/Subscriptions';
+import SiteSettings from '/imports/api/SiteSettings/SiteSettings';
+import Perlin from 'loms.perlin';
+import L from 'leaflet-headless';
+import calcUnion from '/imports/ui/components/Maps/SubsUnion/Unify';
+
+// sudo apt-get install libcairo2-dev libjpeg-dev libgif-dev
+
+Meteor.startup(() => {
+ Perlin.seed(Math.random());
+
+ const addNoisy = (osub) => {
+ const sub = osub;
+ let lat = Math.round(sub.location.lat * 10) / 10;
+ let lon = Math.round(sub.location.lon * 10) / 10;
+ const noiseBase = Perlin.perlin2(lat, lon);
+ const noise = Math.abs(noiseBase / 3);
+ lat += noise;
+ lon += noise;
+ sub.location.lat = lat;
+ sub.location.lon = lon;
+ sub.distance += noiseBase;
+ return sub;
+ };
+
+ const process = () => {
+ const group = new L.FeatureGroup();
+ const result = calcUnion(Subscriptions.find().fetch(), group, addNoisy);
+ const union = result[0];
+ const bounds = result[1];
+
+ if (typeof union === 'object') {
+ const unionSet = {
+ $set: {
+ name: 'subs-public-union',
+ value: JSON.stringify(union),
+ isPublic: true,
+ description: 'Public subscriptions union',
+ type: 'string'
+ }
+ };
+ const boundsSet = {
+ $set: {
+ name: 'subs-public-union-bounds',
+ value: JSON.stringify(bounds),
+ isPublic: true,
+ description: 'Public subscriptions union bounds',
+ type: 'string'
+ }
+ };
+ // FIXME, take care of object size:
+ // https://stackoverflow.com/questions/10827812/what-is-the-length-maximum-for-a-string-data-type-in-mongodb-used-with-ruby
+ SiteSettings.upsert({ name: 'subs-public-union' }, unionSet, { multi: false });
+ SiteSettings.upsert({ name: 'subs-public-union-bounds' }, boundsSet, { multi: false });
+ if (Meteor.isDevelopment) console.log('Subscription union calculated');
+ } else {
+ console.log('Subscription union failed!');
+ }
+ };
+
+ // At startup
+ process();
+
+ Subscriptions.find({ createdAt: { $gt: new Date() } }).observe({
+ added: function newSubAdded() { // doc) {
+ if (Meteor.isDevelopment) console.log('Subs added so recreate union');
+ process();
+ }
+ });
+
+ Subscriptions.find().observe({
+ changed: function subsChanged() { // updatedDoc, oldDoc) {
+ if (Meteor.isDevelopment) console.log('Subs changed so recreate union');
+ process();
+ },
+ removed: function subsRemoved() { // oldDoc) {
+ if (Meteor.isDevelopment) console.log('Subs removed so recreate union');
+ process();
+ }
+ });
+});
diff --git a/imports/ui/components/Maps/SubsUnion/SubsUnion.js b/imports/ui/components/Maps/SubsUnion/SubsUnion.js
index 60cbd26..c624759 100644
--- a/imports/ui/components/Maps/SubsUnion/SubsUnion.js
+++ b/imports/ui/components/Maps/SubsUnion/SubsUnion.js
@@ -3,89 +3,43 @@
/* eslint-disable import/no-absolute-path */
/* global L */
-import { Map } from 'react-leaflet';
-import LGeo from 'leaflet-geodesy';
-import tunion from '@turf/union';
-import ttrunc from '@turf/truncate';
-import { check, Match } from 'meteor/check';
-
-// https://stackoverflow.com/questions/35394577/leaflet-js-union-merge-circles
-const truncOptions = { precision: 6, coordinates: 2 };
-
-function unify(polyList) {
- let unionTemp;
- for (let i = 0; i < polyList.length; i += 1) {
- const pol = polyList[i].toGeoJSON();
- const cleanPol = ttrunc(pol, truncOptions);
- if (i === 0) {
- unionTemp = cleanPol;
- } else {
- unionTemp = ttrunc(tunion(unionTemp, cleanPol), truncOptions);
- }
- }
- return unionTemp;
-}
+import calcUnion from '/imports/ui/components/Maps/SubsUnion/Unify';
const subsUnion = (union, options) => {
- // check(union, Match.Optional(Object));
- check(options, {
- map: Map,
- show: Boolean,
- subs: [Object],
- color: Match.Optional(String),
- fillcolor: Match.Optional(String),
- opacity: Match.Optional(Number),
- fit: Boolean
- });
-
const color = options.color || '#145A32';
const fillColor = options.fillColor || 'green';
const opacity = options.options || 0.1;
if (options.subs) {
const lmap = options.map.leafletElement;
- // http://leafletjs.com/reference-1.2.0.html#layergroup
- // FeatureGroup has getBounds
- const unionGroup = new L.FeatureGroup();
-
if (union) {
lmap.removeLayer(union);
}
union = null;
-
- if (options.subs.length > 0 && options.show) {
- // http://leafletjs.com/reference-1.2.0.html#path
- const copts = {
- parts: 144
- };
- options.subs.forEach((sub) => {
- try {
- if (sub.location && sub.location.lat && sub.location.lon && sub.distance) {
- check(sub.location.lon, Number);
- check(sub.location.lat, Number);
- check(sub.distance, Number);
- const circle = LGeo.circle([sub.location.lat, sub.location.lon], sub.distance * 1000, copts);
- circle.addTo(unionGroup);
- } else {
- console.error(`Wrong subscription ${JSON.stringify(sub)}`);
- }
- } catch (e) {
- console.error(e);
- console.error(`Wrong subscription trying to make union ${JSON.stringify(sub)}`);
+ if (options.show) {
+ if (options.fromServer) {
+ // We get the json from server side
+ union = L.geoJson(JSON.parse(options.subs));
+ union.setStyle({ color, fillColor, fillOpacity: opacity });
+ union.addTo(lmap);
+ if (options.fit && options.bounds) {
+ // console.log(options.bounds);
+ const bounds = JSON.parse(options.bounds);
+ options.map.leafletElement.fitBounds(L.latLngBounds(bounds._northEast, bounds._southWest));
+ }
+ } else if (options.subs.length > 0) {
+ const unionGroup = new L.FeatureGroup();
+ const result = calcUnion(options.subs, unionGroup, sub => sub);
+ const unionJson = result[0];
+ const bounds = result[1];
+
+ union = L.geoJson(unionJson);
+ union.setStyle({ color, fillColor, fillOpacity: opacity });
+ union.addTo(lmap);
+ if (options.fit) {
+ options.map.leafletElement.fitBounds(bounds);
}
- });
- const unionJson = unify(unionGroup.getLayers());
- union = L.geoJson(unionJson);
- union.setStyle({
- color,
- fillColor,
- fillOpacity: opacity
- });
- union.addTo(lmap);
- if (options.fit) {
- options.map.leafletElement.fitBounds(unionGroup.getBounds());
}
- return union;
}
}
return union;
diff --git a/imports/ui/components/Maps/SubsUnion/Unify.js b/imports/ui/components/Maps/SubsUnion/Unify.js
new file mode 100644
index 0000000..68f602a
--- /dev/null
+++ b/imports/ui/components/Maps/SubsUnion/Unify.js
@@ -0,0 +1,48 @@
+import { check } from 'meteor/check';
+import LGeo from 'leaflet-geodesy';
+import tunion from '@turf/union';
+import ttrunc from '@turf/truncate';
+
+// https://stackoverflow.com/questions/35394577/leaflet-js-union-merge-circles
+const truncOptions = { precision: 6, coordinates: 2 };
+
+const unify = (polyList) => {
+ let unionTemp;
+ for (let i = 0; i < polyList.length; i += 1) {
+ const pol = polyList[i].toGeoJSON();
+ const cleanPol = ttrunc(pol, truncOptions);
+ if (i === 0) {
+ unionTemp = cleanPol;
+ } else {
+ unionTemp = ttrunc(tunion(unionTemp, cleanPol), truncOptions);
+ }
+ }
+ return unionTemp;
+};
+
+const calcUnion = (subs, group, decorated) => {
+ const unionGroup = group;
+ const copts = {
+ parts: 144
+ };
+ subs.forEach((osub) => {
+ try {
+ if (osub.location && osub.location.lat && osub.location.lon && osub.distance) {
+ check(osub.location.lon, Number);
+ check(osub.location.lat, Number);
+ check(osub.distance, Number);
+ const dsub = decorated(osub);
+ const circle = LGeo.circle([dsub.location.lat, dsub.location.lon], dsub.distance * 1000, copts);
+ circle.addTo(unionGroup);
+ } else {
+ console.error(`Wrong subscription ${JSON.stringify(osub)}`);
+ }
+ } catch (e) {
+ console.error(e, `Wrong subscription trying to make union ${JSON.stringify(osub)}`);
+ }
+ });
+ const unionJson = unify(unionGroup.getLayers());
+ return [unionJson, unionGroup.getBounds()];
+};
+
+export default calcUnion;
diff --git a/imports/ui/pages/FiresMap/FiresMap.js b/imports/ui/pages/FiresMap/FiresMap.js
index b75d26b..f577e08 100644
--- a/imports/ui/pages/FiresMap/FiresMap.js
+++ b/imports/ui/pages/FiresMap/FiresMap.js
@@ -29,7 +29,6 @@ import ActiveFiresCollection from '/imports/api/ActiveFires/ActiveFires';
import FireAlertsCollection from '/imports/api/FireAlerts/FireAlerts';
import FalsePositivesCollection from '/imports/api/FalsePositives/FalsePositives';
import SiteSettings from '/imports/api/SiteSettings/SiteSettings';
-import UserSubsToFiresCollection from '/imports/api/Subscriptions/Subscriptions';
import { isNotHomeAndMobile, isChrome } from '/imports/ui/components/Utils/isMobile';
import { isHome } from '/imports/ui/components/Utils/location';
import ShareIt from '/imports/ui/components/ShareIt/ShareIt';
@@ -174,6 +173,8 @@ class FiresMap extends React.Component {
map,
subs: this.props.userSubs,
show: this.state.showSubsUnion,
+ bounds: this.props.userSubsBounds,
+ fromServer: true,
fit: false
});
}
@@ -181,7 +182,7 @@ class FiresMap extends React.Component {
render() {
const { t } = this.props;
- console.log(`Rendering ${this.props.loading ? 'loading' : 'LOADED'} map ${this.props.activefires.length + this.props.firealerts.length} of ${this.props.activefirestotal} total. False positives: ${this.props.falsePositives.length}. Subs users ready ${this.props.subsready} (${this.props.userSubs.length}), reactive ${this.state.viewport.zoom >= MAXZOOMREACTIVE}`);
+ console.log(`Rendering ${this.props.loading ? 'loading' : 'LOADED'} map ${this.props.activefires.length + this.props.firealerts.length} of ${this.props.activefirestotal} total. False positives: ${this.props.falsePositives.length}. Reactive ${this.state.viewport.zoom >= MAXZOOMREACTIVE}`);
const title = `${t('AppName')}: ${t('Fuegos activos')}`;
if (Meteor.isDevelopment) {
console.log(`False positives total: ${this.props.falsePositivesTotal}`);
@@ -315,7 +316,8 @@ class FiresMap extends React.Component {
FiresMap.propTypes = {
loading: PropTypes.bool.isRequired,
subsready: PropTypes.bool.isRequired,
- userSubs: PropTypes.arrayOf(PropTypes.object).isRequired,
+ userSubs: PropTypes.string,
+ userSubsBounds: PropTypes.string,
activefires: PropTypes.arrayOf(PropTypes.object).isRequired,
firealerts: PropTypes.arrayOf(PropTypes.object).isRequired,
falsePositives: PropTypes.arrayOf(PropTypes.object).isRequired,
@@ -379,15 +381,17 @@ export default translate([], { wait: true })(withTracker(() => {
Meteor.subscribe('activefirestotal');
Meteor.subscribe('falsePositivesTotal');
- Meteor.subscribe('settings');
- const userSubs = Meteor.subscribe('userSubsToFires');
+ const settingsSubs = Meteor.subscribe('settings');
const lastCheck = SiteSettings.findOne({ name: 'last-fire-check' });
+ const userSubs = SiteSettings.findOne({ name: 'subs-public-union' });
+ const userSubsBounds = SiteSettings.findOne({ name: 'subs-public-union-bounds' });
const fireAlerts = FireAlertsCollection.find().fetch();
const falsePositives = FalsePositivesCollection.find().fetch();
return {
- loading: !subscription ? true : !subscription.ready(),
- userSubs: UserSubsToFiresCollection.find().fetch(),
- subsready: userSubs.ready(),
+ loading: !subscription ? true : !(subscription.ready() && settingsSubs.ready()),
+ userSubs: userSubs ? userSubs.value : null,
+ userSubsBounds: userSubs ? userSubsBounds.value : null,
+ subsready: settingsSubs.ready(),
// Not reactive query depending on zoom level
activefires: ActiveFiresCollection.find({}, { reactive: zoom.get() >= MAXZOOMREACTIVE }).fetch(),
activefirestotal: Counter.get('countActiveFires') + fireAlerts.length,
diff --git a/imports/ui/pages/Subscriptions/SubscriptionsMap.js b/imports/ui/pages/Subscriptions/SubscriptionsMap.js
index ce675fd..5d162a9 100644
--- a/imports/ui/pages/Subscriptions/SubscriptionsMap.js
+++ b/imports/ui/pages/Subscriptions/SubscriptionsMap.js
@@ -19,7 +19,7 @@ import CenterInMyPosition from '/imports/ui/components/CenterInMyPosition/Center
import subsUnion from '/imports/ui/components/Maps/SubsUnion/SubsUnion';
import DefMapLayers from '/imports/ui/components/Maps/DefMapLayers';
import Loading from '/imports/ui/components/Loading/Loading';
-import UserSubsToFiresCollection from '/imports/api/Subscriptions/Subscriptions';
+import SiteSettings from '/imports/api/SiteSettings/SiteSettings';
import { isChrome } from '/imports/ui/components/Utils/isMobile';
import { isHome } from '/imports/ui/components/Utils/location';
import ShareIt from '/imports/ui/components/ShareIt/ShareIt';
@@ -66,6 +66,8 @@ class SubscriptionsMap extends React.Component {
this.state.union = subsUnion(this.state.union, {
map,
subs: this.props.userSubs,
+ bounds: this.props.userSubsBounds,
+ fromServer: true,
show: true,
fit: this.state.init
});
@@ -89,7 +91,7 @@ class SubscriptionsMap extends React.Component {
render() {
const { t } = this.props;
const title = `${t('AppName')}: ${t('Zonas vigiladas')}`;
- console.log(`Rendering Subs users ready ${this.props.subsready} subs: ${this.props.userSubs.length} viewport: ${JSON.stringify(this.state.viewport)}`);
+ console.log(`Rendering Subs users ready ${this.props.subsready} viewport: ${JSON.stringify(this.state.viewport)}`);
return (
{ !isHome() &&
@@ -153,16 +155,20 @@ class SubscriptionsMap extends React.Component {
}
SubscriptionsMap.propTypes = {
+ userSubs: PropTypes.string,
+ userSubsBounds: PropTypes.string,
subsready: PropTypes.bool.isRequired,
- userSubs: PropTypes.arrayOf(PropTypes.object).isRequired,
history: PropTypes.object.isRequired,
t: PropTypes.func.isRequired
};
export default translate([], { wait: true })(withTracker(() => {
- const userSubs = Meteor.subscribe('userSubsToFires');
+ const settingsSubs = Meteor.subscribe('settings');
+ const userSubs = SiteSettings.findOne({ name: 'subs-public-union' });
+ const userSubsBounds = SiteSettings.findOne({ name: 'subs-public-union-bounds' });
return {
- userSubs: UserSubsToFiresCollection.find().fetch(),
- subsready: userSubs.ready()
+ userSubs: userSubs ? userSubs.value : null,
+ userSubsBounds: userSubs ? userSubsBounds.value : null,
+ subsready: settingsSubs.ready()
};
})(SubscriptionsMap));
diff --git a/package-lock.json b/package-lock.json
index 5581dfe..174ad76 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -60,8 +60,7 @@
"abab": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz",
- "integrity": "sha1-X6rZwsB/YN12dw9xzwJbYqY8/U4=",
- "dev": true
+ "integrity": "sha1-X6rZwsB/YN12dw9xzwJbYqY8/U4="
},
"acorn": {
"version": "5.2.1",
@@ -282,8 +281,7 @@
"array-equal": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz",
- "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=",
- "dev": true
+ "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM="
},
"array-includes": {
"version": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz",
@@ -2247,6 +2245,14 @@
"integrity": "sha1-H5CV8u/UlA4LpsWZKreptkzDW6Q=",
"dev": true
},
+ "canvas": {
+ "version": "1.6.9",
+ "resolved": "https://registry.npmjs.org/canvas/-/canvas-1.6.9.tgz",
+ "integrity": "sha1-4/lc7HsWvy1vP8clwC2UDTJY9ps=",
+ "requires": {
+ "nan": "2.6.2"
+ }
+ },
"caseless": {
"version": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
@@ -2778,8 +2784,7 @@
"content-type-parser": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/content-type-parser/-/content-type-parser-1.0.2.tgz",
- "integrity": "sha512-lM4l4CnMEwOLHAHr/P6MEZwZFPJFtAAKgL6pogbXmVZggIqXhdB6RbBtPOTsw2FcXwYhehRGERJmRrjOiIB8pQ==",
- "dev": true
+ "integrity": "sha512-lM4l4CnMEwOLHAHr/P6MEZwZFPJFtAAKgL6pogbXmVZggIqXhdB6RbBtPOTsw2FcXwYhehRGERJmRrjOiIB8pQ=="
},
"convert-source-map": {
"version": "1.5.1",
@@ -2909,14 +2914,12 @@
"cssom": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.2.tgz",
- "integrity": "sha1-uANhcMefB6kP8vFuIihAJ6JDhIs=",
- "dev": true
+ "integrity": "sha1-uANhcMefB6kP8vFuIihAJ6JDhIs="
},
"cssstyle": {
"version": "0.2.37",
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-0.2.37.tgz",
"integrity": "sha1-VBCXI0yyUTyDzu06zdwn/yeYfVQ=",
- "dev": true,
"requires": {
"cssom": "0.3.2"
}
@@ -2930,6 +2933,11 @@
"es5-ext": "0.10.37"
}
},
+ "d3-queue": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/d3-queue/-/d3-queue-2.0.3.tgz",
+ "integrity": "sha1-B/vaOsrlNYqcUpmq+ICt8JU+0sI="
+ },
"damerau-levenshtein": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz",
@@ -3331,7 +3339,6 @@
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.9.0.tgz",
"integrity": "sha512-v0MYvNQ32bzwoG2OSFzWAkuahDQHK92JBN0pTAALJ4RIxEZe766QJPDR8Hqy7XNUy5K3fnVL76OqYAdc4TZEIw==",
- "dev": true,
"requires": {
"esprima": "3.1.3",
"estraverse": "4.2.0",
@@ -3343,38 +3350,32 @@
"deep-is": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
- "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
- "dev": true
+ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ="
},
"esprima": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz",
- "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=",
- "dev": true
+ "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM="
},
"estraverse": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
- "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=",
- "dev": true
+ "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM="
},
"esutils": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
- "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
- "dev": true
+ "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs="
},
"fast-levenshtein": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
- "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
- "dev": true
+ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
},
"levn": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
"integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
- "dev": true,
"requires": {
"prelude-ls": "1.1.2",
"type-check": "0.3.2"
@@ -3384,7 +3385,6 @@
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
"integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
- "dev": true,
"requires": {
"deep-is": "0.1.3",
"fast-levenshtein": "2.0.6",
@@ -3397,21 +3397,18 @@
"prelude-ls": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
- "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
- "dev": true
+ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ="
},
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
- "dev": true,
"optional": true
},
"type-check": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
"integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
- "dev": true,
"requires": {
"prelude-ls": "1.1.2"
}
@@ -3419,8 +3416,7 @@
"wordwrap": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
- "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
- "dev": true
+ "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
}
}
},
@@ -4941,7 +4937,6 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz",
"integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==",
- "dev": true,
"requires": {
"whatwg-encoding": "1.0.3"
}
@@ -7451,6 +7446,92 @@
"resolved": "https://registry.npmjs.org/leaflet-graphicscale/-/leaflet-graphicscale-0.0.2.tgz",
"integrity": "sha1-q2INKJ6acETC9RB8g74XhDrsUwM="
},
+ "leaflet-headless": {
+ "version": "0.2.6",
+ "resolved": "https://registry.npmjs.org/leaflet-headless/-/leaflet-headless-0.2.6.tgz",
+ "integrity": "sha1-Hqh4c/fuJj/JyI3xXsx4qZUdcsk=",
+ "requires": {
+ "canvas": "1.6.9",
+ "jsdom": "9.8.3",
+ "leaflet": "1.3.1",
+ "leaflet-image": "0.4.0",
+ "request": "2.83.0"
+ },
+ "dependencies": {
+ "acorn": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-2.7.0.tgz",
+ "integrity": "sha1-q259nYhqrKiwhbwzEreaGYQz8Oc="
+ },
+ "acorn-globals": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-1.0.9.tgz",
+ "integrity": "sha1-VbtemGkVB7dFedBRNBMhfDgMVM8=",
+ "requires": {
+ "acorn": "2.7.0"
+ }
+ },
+ "iconv-lite": {
+ "version": "0.4.19",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
+ "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ=="
+ },
+ "jsdom": {
+ "version": "9.8.3",
+ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-9.8.3.tgz",
+ "integrity": "sha1-/eKcEJwyoRMeC2xlkU5kGY+Xw3A=",
+ "requires": {
+ "abab": "1.0.4",
+ "acorn": "2.7.0",
+ "acorn-globals": "1.0.9",
+ "array-equal": "1.0.0",
+ "content-type-parser": "1.0.2",
+ "cssom": "0.3.2",
+ "cssstyle": "0.2.37",
+ "escodegen": "1.9.0",
+ "html-encoding-sniffer": "1.0.2",
+ "iconv-lite": "0.4.19",
+ "nwmatcher": "1.4.3",
+ "parse5": "1.5.1",
+ "request": "2.83.0",
+ "sax": "1.2.4",
+ "symbol-tree": "3.2.2",
+ "tough-cookie": "2.3.3",
+ "webidl-conversions": "3.0.1",
+ "whatwg-encoding": "1.0.3",
+ "whatwg-url": "3.1.0",
+ "xml-name-validator": "2.0.1"
+ }
+ },
+ "parse5": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-1.5.1.tgz",
+ "integrity": "sha1-m387DeMr543CQBsXVzzK8Pb1nZQ="
+ },
+ "webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
+ },
+ "whatwg-url": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-3.1.0.tgz",
+ "integrity": "sha1-e9yuSQ+SGu9kUftnOexrvY6Qe/Y=",
+ "requires": {
+ "tr46": "0.0.3",
+ "webidl-conversions": "3.0.1"
+ }
+ }
+ }
+ },
+ "leaflet-image": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/leaflet-image/-/leaflet-image-0.4.0.tgz",
+ "integrity": "sha1-6E8i/2KI8JubDi9RpIUKnweWjME=",
+ "requires": {
+ "d3-queue": "2.0.3"
+ }
+ },
"leaflet-sleep": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/leaflet-sleep/-/leaflet-sleep-0.5.1.tgz",
@@ -9028,8 +9109,7 @@
"nwmatcher": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/nwmatcher/-/nwmatcher-1.4.3.tgz",
- "integrity": "sha512-IKdSTiDWCarf2JTS5e9e2+5tPZGdkRJ79XjYV0pzK8Q9BpsFyBq1RGKxzs7Q8UBushGw7m6TzVKz6fcY99iSWw==",
- "dev": true
+ "integrity": "sha512-IKdSTiDWCarf2JTS5e9e2+5tPZGdkRJ79XjYV0pzK8Q9BpsFyBq1RGKxzs7Q8UBushGw7m6TzVKz6fcY99iSWw=="
},
"oauth-sign": {
"version": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
@@ -10887,8 +10967,7 @@
"sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
- "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
- "dev": true
+ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
},
"semver": {
"version": "5.4.1",
@@ -11291,8 +11370,7 @@
"symbol-tree": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz",
- "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=",
- "dev": true
+ "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY="
},
"table": {
"version": "4.0.2",
@@ -11479,8 +11557,7 @@
"tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
- "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=",
- "dev": true
+ "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
},
"trim-right": {
"version": "1.0.1",
@@ -11688,7 +11765,6 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.3.tgz",
"integrity": "sha512-jLBwwKUhi8WtBfsMQlL4bUUcT8sMkAtQinscJAe/M4KHCkHuUJAF6vuB0tueNIw4c8ziO6AkRmgY+jL3a0iiPw==",
- "dev": true,
"requires": {
"iconv-lite": "0.4.19"
},
@@ -11696,8 +11772,7 @@
"iconv-lite": {
"version": "0.4.19",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
- "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==",
- "dev": true
+ "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ=="
}
}
},
@@ -11865,8 +11940,7 @@
"xml-name-validator": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-2.0.1.tgz",
- "integrity": "sha1-TYuPHszTQZqjYgYb7O9RXh5VljU=",
- "dev": true
+ "integrity": "sha1-TYuPHszTQZqjYgYb7O9RXh5VljU="
},
"xtend": {
"version": "4.0.1",
diff --git a/package.json b/package.json
index 3fd1c7b..43ffaf6 100644
--- a/package.json
+++ b/package.json
@@ -37,6 +37,7 @@
"leaflet": "^1.3.1",
"leaflet-geodesy": "^0.2.1",
"leaflet-graphicscale": "0.0.2",
+ "leaflet-headless": "^0.2.6",
"leaflet-sleep": "^0.5.1",
"lodash": "^4.17.4",
"loms.perlin": "^1.0.1",
From 4f9e0cc63da333600df44e0a089474ab70e8944a Mon Sep 17 00:00:00 2001
From: vjrj
Date: Thu, 15 Feb 2018 12:48:33 +0100
Subject: [PATCH 039/330] Added prerender
---
.meteor/packages | 1 +
.meteor/versions | 1 +
2 files changed, 2 insertions(+)
diff --git a/.meteor/packages b/.meteor/packages
index de1e606..823ac22 100644
--- a/.meteor/packages
+++ b/.meteor/packages
@@ -62,3 +62,4 @@ barbatus:stars-rating
arkham:comments-ui
facts
gadicohen:sitemaps
+dferber:prerender
diff --git a/.meteor/versions b/.meteor/versions
index e9ae166..f6a33ef 100644
--- a/.meteor/versions
+++ b/.meteor/versions
@@ -35,6 +35,7 @@ ddp-common@1.3.0
ddp-rate-limiter@1.0.7
ddp-server@2.1.0
deps@1.0.12
+dferber:prerender@2.2.2_3
diff-sequence@1.0.7
dynamic-import@0.2.0
ecmascript@0.9.0
From dbfbba1067dfb03060fe30711ca69b93dd98ea43 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Thu, 15 Feb 2018 13:27:09 +0100
Subject: [PATCH 040/330] Added unstranslated cookie message
---
imports/startup/client/i18n.js | 2 +-
public/locales/en/common.json | 3 ++-
public/locales/es/common.json | 3 ++-
3 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/imports/startup/client/i18n.js b/imports/startup/client/i18n.js
index 63abf76..69771e2 100644
--- a/imports/startup/client/i18n.js
+++ b/imports/startup/client/i18n.js
@@ -90,7 +90,7 @@ i18n.use(backend)
position: 'bottom',
linkText: 'Lee más',
linkRouteName: '/privacy',
- acceptButtonText: 'Aceptar',
+ acceptButtonText: t('Aceptar'),
html: false,
expirationInDays: 70,
forceShow: false
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index 7c2740b..fd4fcd3 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -189,5 +189,6 @@
"Elige un tipo":
"Choose a type",
"Puedes participar en las traducciones":
- "You can help with the translations"
+ "You can help with the translations",
+ "Aceptar": "Accept"
}
diff --git a/public/locales/es/common.json b/public/locales/es/common.json
index 5d1a46d..49ae9e0 100644
--- a/public/locales/es/common.json
+++ b/public/locales/es/common.json
@@ -276,5 +276,6 @@
"Elige un tipo":
"Elige un tipo",
"Puedes participar en las traducciones":
- "Puedes participar en las traducciones"
+ "Puedes participar en las traducciones",
+ "Aceptar": "Aceptar"
}
From 79bdc189347c1559d1d15bd216d9b0ece5f825ee Mon Sep 17 00:00:00 2001
From: vjrj
Date: Thu, 15 Feb 2018 16:53:12 +0100
Subject: [PATCH 041/330] Migrated to prerender node-module
---
.meteor/packages | 1 -
.meteor/versions | 1 -
client/main.html | 1 +
imports/startup/server/index.js | 1 +
imports/startup/server/prerender.js | 46 ++++++
package-lock.json | 232 +++++++++++++++++++++++++++-
package.json | 1 +
7 files changed, 278 insertions(+), 5 deletions(-)
create mode 100644 imports/startup/server/prerender.js
diff --git a/.meteor/packages b/.meteor/packages
index 823ac22..de1e606 100644
--- a/.meteor/packages
+++ b/.meteor/packages
@@ -62,4 +62,3 @@ barbatus:stars-rating
arkham:comments-ui
facts
gadicohen:sitemaps
-dferber:prerender
diff --git a/.meteor/versions b/.meteor/versions
index f6a33ef..e9ae166 100644
--- a/.meteor/versions
+++ b/.meteor/versions
@@ -35,7 +35,6 @@ ddp-common@1.3.0
ddp-rate-limiter@1.0.7
ddp-server@2.1.0
deps@1.0.12
-dferber:prerender@2.2.2_3
diff-sequence@1.0.7
dynamic-import@0.2.0
ecmascript@0.9.0
diff --git a/client/main.html b/client/main.html
index 90fe4b1..5f2bc53 100644
--- a/client/main.html
+++ b/client/main.html
@@ -1,5 +1,6 @@
+
All against Fire | Tod@s contra el Fuego
diff --git a/imports/startup/server/index.js b/imports/startup/server/index.js
index b331b73..2a0cebb 100644
--- a/imports/startup/server/index.js
+++ b/imports/startup/server/index.js
@@ -10,3 +10,4 @@ import './facts';
import '../common/comments';
import './sitemaps';
import './subsUnion';
+import './prerender';
diff --git a/imports/startup/server/prerender.js b/imports/startup/server/prerender.js
new file mode 100644
index 0000000..4942165
--- /dev/null
+++ b/imports/startup/server/prerender.js
@@ -0,0 +1,46 @@
+/* global WebApp */
+
+import { Meteor } from 'meteor/meteor';
+import prerenderIO from 'prerender-node';
+
+// NOTE: Fetch as Google needs the /?_escaped_fragment_=
+// https://webmasters.stackexchange.com/questions/72819/fetch-as-google-doesnt-find-the-html-snapshot-for-my-ajax-content
+
+// https://forums.meteor.com/t/any-guide-for-prerender-node-and-your-own-server-on-meteor/22054/9
+const settings = Meteor.settings.PrerenderIO;
+
+// https://github.com/dferber90/meteor-prerender/blob/master/server/prerender.js
+const token = process.env.PRERENDERIO_TOKEN || (settings && settings.token);
+const protocol = process.env.PRERENDERIO_PROTOCOL || (settings && settings.protocol);
+// service url (support `prerenderServiceUrl` (for historical reasons) and `serviceUrl`)
+let serviceUrl = settings && (settings.prerenderServiceUrl || settings.serviceUrl);
+serviceUrl = process.env.PRERENDERIO_SERVICE_URL || serviceUrl;
+
+Meteor.startup(() => {
+ /* TODO if (!__meteor_runtime_config__.ROOT_URL.match(/www|stg|app/)) return; */
+
+ prerenderIO.set('prerenderToken', token);
+
+ if (serviceUrl) prerenderIO.set('prerenderServiceUrl', serviceUrl);
+ prerenderIO.set('prerenderToken', token);
+ if (protocol) prerenderIO.set('protocol', protocol);
+
+ prerenderIO.set('beforeRender', (req, done) => {
+ if (Meteor.isDevelopment) console.log('\nprerender before', req.headers, '\n\n');
+ /* This method is intended to be used for caching, but could be used to save analytics or anything else you need to do for each crawler request. If you return a string from beforeRender, the middleware will serve that to the crawler (with status 200) instead of making a request to the prerender service. If you return an object the middleware will look for a status and body property (defaulting to 200 and "" respectively) and serve those instead. */
+ done();
+ });
+
+ prerenderIO.set('afterRender', (err, req, prerender_res) => {
+ /* This method is intended to be used for caching, but could be used to save analytics or anything else you need to do for each crawler request. This method is a noop and is called after the prerender service returns HTML. */
+ if (err) {
+ console.log('prerenderio error', err);
+ return;
+ }
+ if (Meteor.isDevelopment) console.log('prerender after', req.url, '\nheaders:', req.headers, '\nres complete:', prerender_res.complete, prerender_res.statusCode, prerender_res.statusMessage, '\nres headers:', prerender_res.headers, '\nres body', prerender_res);
+ });
+
+ WebApp.rawConnectHandlers.use(prerenderIO);
+
+ console.log('\nprerender service:', settings);
+});
diff --git a/package-lock.json b/package-lock.json
index 174ad76..7aeb4b8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7313,7 +7313,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz",
"integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=",
- "dev": true,
"requires": {
"jsonify": "0.0.0"
}
@@ -7337,8 +7336,7 @@
"jsonify": {
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
- "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=",
- "dev": true
+ "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM="
},
"jsonp": {
"version": "0.2.1",
@@ -9654,6 +9652,234 @@
"integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
"dev": true
},
+ "prerender-node": {
+ "version": "2.7.4",
+ "resolved": "https://registry.npmjs.org/prerender-node/-/prerender-node-2.7.4.tgz",
+ "integrity": "sha1-L7PEHRRHjxemCfWpAM0XvwDjy3E=",
+ "requires": {
+ "request": "2.81.0"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "4.11.8",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz",
+ "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=",
+ "requires": {
+ "co": "4.6.0",
+ "json-stable-stringify": "1.0.1"
+ }
+ },
+ "assert-plus": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz",
+ "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ="
+ },
+ "asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
+ },
+ "aws-sign2": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz",
+ "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8="
+ },
+ "aws4": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz",
+ "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4="
+ },
+ "boom": {
+ "version": "2.10.1",
+ "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz",
+ "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=",
+ "requires": {
+ "hoek": "2.16.3"
+ }
+ },
+ "caseless": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
+ },
+ "co": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
+ },
+ "combined-stream": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz",
+ "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=",
+ "requires": {
+ "delayed-stream": "1.0.0"
+ }
+ },
+ "cryptiles": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz",
+ "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=",
+ "requires": {
+ "boom": "2.10.1"
+ }
+ },
+ "delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
+ },
+ "extend": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
+ "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ="
+ },
+ "forever-agent": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
+ },
+ "form-data": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz",
+ "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=",
+ "requires": {
+ "asynckit": "0.4.0",
+ "combined-stream": "1.0.6",
+ "mime-types": "2.1.17"
+ }
+ },
+ "har-schema": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz",
+ "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4="
+ },
+ "har-validator": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz",
+ "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=",
+ "requires": {
+ "ajv": "4.11.8",
+ "har-schema": "1.0.5"
+ }
+ },
+ "hawk": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz",
+ "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=",
+ "requires": {
+ "boom": "2.10.1",
+ "cryptiles": "2.0.5",
+ "hoek": "2.16.3",
+ "sntp": "1.0.9"
+ }
+ },
+ "hoek": {
+ "version": "2.16.3",
+ "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz",
+ "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0="
+ },
+ "http-signature": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz",
+ "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=",
+ "requires": {
+ "assert-plus": "0.2.0",
+ "jsprim": "1.4.1",
+ "sshpk": "1.13.1"
+ }
+ },
+ "is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
+ },
+ "isstream": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
+ },
+ "json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
+ },
+ "oauth-sign": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
+ "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM="
+ },
+ "performance-now": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz",
+ "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU="
+ },
+ "qs": {
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz",
+ "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM="
+ },
+ "request": {
+ "version": "2.81.0",
+ "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz",
+ "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=",
+ "requires": {
+ "aws-sign2": "0.6.0",
+ "aws4": "1.6.0",
+ "caseless": "0.12.0",
+ "combined-stream": "1.0.6",
+ "extend": "3.0.1",
+ "forever-agent": "0.6.1",
+ "form-data": "2.1.4",
+ "har-validator": "4.2.1",
+ "hawk": "3.1.3",
+ "http-signature": "1.1.1",
+ "is-typedarray": "1.0.0",
+ "isstream": "0.1.2",
+ "json-stringify-safe": "5.0.1",
+ "mime-types": "2.1.17",
+ "oauth-sign": "0.8.2",
+ "performance-now": "0.2.0",
+ "qs": "6.4.0",
+ "safe-buffer": "5.1.1",
+ "stringstream": "0.0.5",
+ "tough-cookie": "2.3.3",
+ "tunnel-agent": "0.6.0",
+ "uuid": "3.2.1"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
+ "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
+ },
+ "sntp": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz",
+ "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=",
+ "requires": {
+ "hoek": "2.16.3"
+ }
+ },
+ "stringstream": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz",
+ "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg="
+ },
+ "tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+ "requires": {
+ "safe-buffer": "5.1.1"
+ }
+ },
+ "uuid": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz",
+ "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA=="
+ }
+ }
+ },
"preserve": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz",
diff --git a/package.json b/package.json
index 43ffaf6..32d4fbb 100644
--- a/package.json
+++ b/package.json
@@ -49,6 +49,7 @@
"node-geocoder": "^3.21.1",
"nodemailer": "^4.4.2",
"popper.js": "^1.12.7",
+ "prerender-node": "^2.7.4",
"prop-types": "^15.6.0",
"push.js": "^1.0.5",
"rc-slider": "^8.5.0",
From aec4c2a0b02150f3491325a7d84f3fc018ba5de3 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Thu, 15 Feb 2018 17:13:54 +0100
Subject: [PATCH 042/330] Added meta hreflang
---
imports/ui/layouts/App/App.js | 2 ++
1 file changed, 2 insertions(+)
diff --git a/imports/ui/layouts/App/App.js b/imports/ui/layouts/App/App.js
index f4cfc36..7e14c91 100644
--- a/imports/ui/layouts/App/App.js
+++ b/imports/ui/layouts/App/App.js
@@ -94,6 +94,8 @@ const App = props => (
{i18n.t('AppName')}
+
+
From 2477c985d6cf95cdefd086cfabbecd6fe1608b78 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Thu, 15 Feb 2018 18:10:38 +0100
Subject: [PATCH 043/330] Added popup to alerts
---
imports/api/Fires/server/publications.js | 19 +++++++++++++++++++
imports/ui/components/Maps/FireList.js | 2 +-
imports/ui/components/Maps/FirePixel.js | 18 ++++++++++++++----
imports/ui/components/Maps/FirePopup.js | 2 +-
imports/ui/layouts/App/App.js | 2 +-
imports/ui/pages/Fires/Fires.js | 7 ++++++-
6 files changed, 42 insertions(+), 8 deletions(-)
diff --git a/imports/api/Fires/server/publications.js b/imports/api/Fires/server/publications.js
index 575c7af..0f0b80c 100644
--- a/imports/api/Fires/server/publications.js
+++ b/imports/api/Fires/server/publications.js
@@ -8,6 +8,7 @@ import { Promise } from 'meteor/promise';
import NodeGeocoder from 'node-geocoder';
import { gmapServerKey } from '/imports/startup/server/IPGeocoder';
import ActiveFiresCollection from '/imports/api/ActiveFires/ActiveFires';
+import FireAlertsCollection from '/imports/api/FireAlerts/FireAlerts';
import FiresCollection from '../Fires';
function findFire(unsealed) {
@@ -70,6 +71,24 @@ const findOrCreateFire = (obj) => {
return findFire(obj);
};
+Meteor.publish('fireFromAlertId', function fireFromAlertId(_id) {
+ try {
+ check(_id, String);
+ // console.log(`Looking for alert fire ${_id}`);
+ const fire = FireAlertsCollection.findOne(new Meteor.Collection.ObjectID(_id));
+ if (fire) {
+ // console.info(`Active fire found: ${_id}`);
+ return findOrCreateFire(fixConfidence(fire));
+ }
+ console.info(`Alert fire not found: ${_id}`);
+ // Not found in active fires!
+ return this.ready();
+ } catch (e) {
+ console.info(`Alert fire not found (with error): ${_id}`);
+ return this.ready();
+ }
+});
+
Meteor.publish('fireFromActiveId', function fireFromActiveId(_id) {
try {
check(_id, String);
diff --git a/imports/ui/components/Maps/FireList.js b/imports/ui/components/Maps/FireList.js
index f4a851c..ecf22f1 100644
--- a/imports/ui/components/Maps/FireList.js
+++ b/imports/ui/components/Maps/FireList.js
@@ -18,7 +18,7 @@ export default function FireList(props) {
if (useMarks) {
items = fires.map(({ _id, ...otherProps }) => ( ));
} else if (usePixel && !falsePositives) {
- items = fires.map(({ _id, ...otherProps }) => ( ));
+ items = fires.map(({ _id, ...otherProps }) => ( ));
} else if (!falsePositives) {
items = fires.map(({ _id, ...otherProps }) => ( ));
}
diff --git a/imports/ui/components/Maps/FirePixel.js b/imports/ui/components/Maps/FirePixel.js
index 57f2092..c81b547 100644
--- a/imports/ui/components/Maps/FirePixel.js
+++ b/imports/ui/components/Maps/FirePixel.js
@@ -3,9 +3,13 @@
import React from 'react';
import { CircleMarker } from 'react-leaflet';
import PropTypes from 'prop-types';
+import FirePopup from './FirePopup';
+import { translate } from 'react-i18next';
/* Less acurate (1 pixel per fire) but faster */
-const FirePixel = ({ lat, lon, nasa }) => (
+const FirePixel = ({
+ lat, lon, nasa, id, when, t, history
+}) => (
(
fillOpacity="1"
fill
radius={nasa ? 1 : 2}
- />
+ >
+
+
);
FirePixel.propTypes = {
lat: PropTypes.number.isRequired,
lon: PropTypes.number.isRequired,
- nasa: PropTypes.bool.isRequired
+ id: PropTypes.object.isRequired,
+ history: PropTypes.object.isRequired,
+ when: PropTypes.instanceOf(Date),
+ nasa: PropTypes.bool.isRequired,
+ t: PropTypes.func.isRequired
};
-export default FirePixel;
+export default translate([], { wait: true })(FirePixel);
diff --git a/imports/ui/components/Maps/FirePopup.js b/imports/ui/components/Maps/FirePopup.js
index 8e7d2a8..c917a6b 100644
--- a/imports/ui/components/Maps/FirePopup.js
+++ b/imports/ui/components/Maps/FirePopup.js
@@ -22,7 +22,7 @@ const FirePopup = ({
{when && {t('Detectado')}: {moment(when).fromNow()} }
{ /* if nasa === null means that the is a false positive fire */ }
- history.push(`/fire/${nasa ? 'active' : 'archive'}/${id}`)}>{t('Más información sobre este fuego')}
+ history.push(`/fire/${nasa ? 'active' : 'alert'}/${id}`)}>{t('Más información sobre este fuego')}
diff --git a/imports/ui/layouts/App/App.js b/imports/ui/layouts/App/App.js
index 7e14c91..e80d8cc 100644
--- a/imports/ui/layouts/App/App.js
+++ b/imports/ui/layouts/App/App.js
@@ -116,7 +116,7 @@ const App = props => (
-
+
diff --git a/imports/ui/pages/Fires/Fires.js b/imports/ui/pages/Fires/Fires.js
index fe423a5..a892181 100644
--- a/imports/ui/pages/Fires/Fires.js
+++ b/imports/ui/pages/Fires/Fires.js
@@ -35,7 +35,7 @@ class Fire extends React.Component {
componentWillReceiveProps(nextProps) {
if (this.props.when !== nextProps.when || this.props.loading !== nextProps.loading || this.props.notfound !== nextProps.notfound) {
// console.log(`Next when ${nextProps.when}`);
- if (nextProps.fire && (nextProps.active || nextProps.fromHash)) {
+ if (nextProps.fire && (nextProps.alert || nextProps.active || nextProps.fromHash)) {
// change url to archive with new _id
nextProps.history.replace(`/fire/archive/${nextProps.fire._id}`);
}
@@ -173,6 +173,7 @@ Fire.propTypes = {
notfound: PropTypes.bool.isRequired,
fromHash: PropTypes.bool.isRequired,
active: PropTypes.bool.isRequired,
+ alert: PropTypes.bool.isRequired,
when: PropTypes.instanceOf(Date),
fire: PropTypes.object
};
@@ -188,10 +189,13 @@ const FireContainer = withTracker(({ match }) => {
let subscription;
const active = fireType === 'active';
const archive = fireType === 'archive';
+ const alert = fireType === 'alert';
let fromHash = false;
if (active) {
subscription = Meteor.subscribe('fireFromActiveId', id);
+ } else if (alert) {
+ subscription = Meteor.subscribe('fireFromAlertId', id);
} else if (archive) {
subscription = Meteor.subscribe('fireFromId', id);
} else {
@@ -209,6 +213,7 @@ const FireContainer = withTracker(({ match }) => {
return {
loading,
active,
+ alert,
fromHash,
fire: FiresCollection.findOne(),
notfound,
From 44428be8aa87c60d1551b96734f9ea1cf8731600 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Thu, 15 Feb 2018 22:35:23 +0100
Subject: [PATCH 044/330] Comments config
---
imports/startup/client/comments.js | 2 +-
imports/startup/common/comments.js | 4 ++++
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/imports/startup/client/comments.js b/imports/startup/client/comments.js
index 62fc868..94cbe90 100644
--- a/imports/startup/client/comments.js
+++ b/imports/startup/client/comments.js
@@ -37,6 +37,6 @@ i18n.init((err, t) => {
template: 'bootstrap', // default 'semantic-ui'
// default 'http://s3.amazonaws.com/37assets/svn/765-default-avatar.png'
defaultAvatar: '/default-avatar.png',
- markdown: false
+ markdown: true
});
});
diff --git a/imports/startup/common/comments.js b/imports/startup/common/comments.js
index 09fa297..728ec88 100644
--- a/imports/startup/common/comments.js
+++ b/imports/startup/common/comments.js
@@ -8,6 +8,10 @@ Comments.config({
publishUserFields: {
profile: 1
},
+ mediaAnalyzers: [
+ Comments.analyzers.image,
+ Comments.analyzers.youtube
+ ],
generateUsername: function genUser(user) {
// console.log(JSON.stringify(user));
// FIXME
From 45a63548c7837de386271635ce18bb1039c075f8 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Fri, 16 Feb 2018 13:12:06 +0100
Subject: [PATCH 045/330] Added raven/sentry to server side
---
imports/startup/client/ravenLogger.js | 3 +--
imports/startup/server/index.js | 1 +
imports/startup/server/ravenLogger.js | 13 +++++++++++++
3 files changed, 15 insertions(+), 2 deletions(-)
create mode 100644 imports/startup/server/ravenLogger.js
diff --git a/imports/startup/client/ravenLogger.js b/imports/startup/client/ravenLogger.js
index 509121a..304b1e8 100644
--- a/imports/startup/client/ravenLogger.js
+++ b/imports/startup/client/ravenLogger.js
@@ -5,9 +5,8 @@ const ravenOptions = {};
const ravenLogger = new RavenLogger({
publicDSN: Meteor.settings.public.sentryPublicDSN, // will be used on the client
- privateDSN: Meteor.settings.sentryPrivateDSN, // will be used on the server
shouldCatchConsoleError: true, // default
- trackUser: false // default
+ trackUser: true // default
}, ravenOptions);
export default ravenLogger;
diff --git a/imports/startup/server/index.js b/imports/startup/server/index.js
index 2a0cebb..6db3588 100644
--- a/imports/startup/server/index.js
+++ b/imports/startup/server/index.js
@@ -1,3 +1,4 @@
+import './ravenLogger';
import './i18n';
import './accounts';
import './api';
diff --git a/imports/startup/server/ravenLogger.js b/imports/startup/server/ravenLogger.js
new file mode 100644
index 0000000..d722dcf
--- /dev/null
+++ b/imports/startup/server/ravenLogger.js
@@ -0,0 +1,13 @@
+import RavenLogger from 'meteor/flowkey:raven';
+import { Meteor } from 'meteor/meteor';
+
+const ravenOptions = {};
+
+const ravenLogger = new RavenLogger({
+ publicDSN: Meteor.settings.public.sentryPublicDSN,
+ privateDSN: Meteor.settings.sentryPrivateDSN,
+ shouldCatchConsoleError: true, // default
+ trackUser: true // default
+}, ravenOptions);
+
+export default ravenLogger;
From c35349670ab92f6a497178282aea36290c0dcd78 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Fri, 16 Feb 2018 16:45:07 +0100
Subject: [PATCH 046/330] Fix test timming
---
cucumber/features/steps_definitions/pages.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/cucumber/features/steps_definitions/pages.js b/cucumber/features/steps_definitions/pages.js
index 5074d52..e309e15 100644
--- a/cucumber/features/steps_definitions/pages.js
+++ b/cucumber/features/steps_definitions/pages.js
@@ -43,6 +43,7 @@ module.exports = function () {
for (let i = 0; i < pages.length; i += 1) {
client.url(`${process.env.ROOT_URL}/${pages[i][0]}`);
const content = pages[i][1];
+ client.waitForVisible('#react-root', 5000);
client.waitForText('#react-root', content);
}
callback();
From 79eed373d41bdcb8e09edd0014eb456435ca0d04 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Fri, 16 Feb 2018 16:46:06 +0100
Subject: [PATCH 047/330] Make subs union no clickable (interactive) and wait
for fire alerts
---
imports/ui/components/Maps/SubsUnion/SubsUnion.js | 11 +++++++++--
imports/ui/pages/FiresMap/FiresMap.js | 5 +++--
2 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/imports/ui/components/Maps/SubsUnion/SubsUnion.js b/imports/ui/components/Maps/SubsUnion/SubsUnion.js
index c624759..ab28b21 100644
--- a/imports/ui/components/Maps/SubsUnion/SubsUnion.js
+++ b/imports/ui/components/Maps/SubsUnion/SubsUnion.js
@@ -9,6 +9,7 @@ const subsUnion = (union, options) => {
const color = options.color || '#145A32';
const fillColor = options.fillColor || 'green';
const opacity = options.options || 0.1;
+ const interactive = options.interactive || false;
if (options.subs) {
const lmap = options.map.leafletElement;
@@ -18,10 +19,14 @@ const subsUnion = (union, options) => {
union = null;
if (options.show) {
if (options.fromServer) {
+ // http://leafletjs.com/reference-1.3.0.html#geojson
// We get the json from server side
union = L.geoJson(JSON.parse(options.subs));
- union.setStyle({ color, fillColor, fillOpacity: opacity });
+ union.setStyle({
+ color, fillColor, fillOpacity: opacity, interactive
+ });
union.addTo(lmap);
+ union.bringToBack();
if (options.fit && options.bounds) {
// console.log(options.bounds);
const bounds = JSON.parse(options.bounds);
@@ -34,7 +39,9 @@ const subsUnion = (union, options) => {
const bounds = result[1];
union = L.geoJson(unionJson);
- union.setStyle({ color, fillColor, fillOpacity: opacity });
+ union.setStyle({
+ color, fillColor, fillOpacity: opacity, interactive
+ });
union.addTo(lmap);
if (options.fit) {
options.map.leafletElement.fitBounds(bounds);
diff --git a/imports/ui/pages/FiresMap/FiresMap.js b/imports/ui/pages/FiresMap/FiresMap.js
index f577e08..3319841 100644
--- a/imports/ui/pages/FiresMap/FiresMap.js
+++ b/imports/ui/pages/FiresMap/FiresMap.js
@@ -336,6 +336,7 @@ let geoInit = true;
export default translate([], { wait: true })(withTracker(() => {
let subscription;
+ let alertSubscription;
const centerStored = store.get('firesmap_center');
const zoomStored = store.get('firesmap_zoom');
@@ -362,7 +363,7 @@ export default translate([], { wait: true })(withTracker(() => {
mapSize.get()[1].lng,
mapSize.get()[1].lat
);
- Meteor.subscribe(
+ alertSubscription = Meteor.subscribe(
'fireAlerts',
mapSize.get()[0].lng,
mapSize.get()[0].lat,
@@ -388,7 +389,7 @@ export default translate([], { wait: true })(withTracker(() => {
const fireAlerts = FireAlertsCollection.find().fetch();
const falsePositives = FalsePositivesCollection.find().fetch();
return {
- loading: !subscription ? true : !(subscription.ready() && settingsSubs.ready()),
+ loading: !subscription ? true : !(subscription.ready() && settingsSubs.ready() && alertSubscription.ready()),
userSubs: userSubs ? userSubs.value : null,
userSubsBounds: userSubs ? userSubsBounds.value : null,
subsready: settingsSubs.ready(),
From ee427758c26d1f1e55db0704fa38ed27edebc966 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Fri, 16 Feb 2018 17:07:18 +0100
Subject: [PATCH 048/330] i18n whitelist langs
---
imports/startup/common/i18n.js | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/imports/startup/common/i18n.js b/imports/startup/common/i18n.js
index e72a81f..9be09fd 100644
--- a/imports/startup/common/i18n.js
+++ b/imports/startup/common/i18n.js
@@ -35,8 +35,7 @@ const i18nOpts = {
return value;
}
},
- whitelist: false,
- // whitelist: ['es', 'en'], // allowed languages
+ whitelist: ['es', 'en'], // allowed languages
load: 'languageOnly', // 'es' o 'en', previously: 'all', // es-ES -> es, en-US -> en
debug: shouldDebug,
ns: 'common',
From eee1abf3edde8afae96ef7de8b29e143d9f17ff0 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Fri, 16 Feb 2018 20:40:17 +0100
Subject: [PATCH 049/330] Fix some minor bug in SubsMap
---
imports/ui/pages/Subscriptions/SubscriptionsMap.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/imports/ui/pages/Subscriptions/SubscriptionsMap.js b/imports/ui/pages/Subscriptions/SubscriptionsMap.js
index 5d162a9..69f5bdf 100644
--- a/imports/ui/pages/Subscriptions/SubscriptionsMap.js
+++ b/imports/ui/pages/Subscriptions/SubscriptionsMap.js
@@ -168,7 +168,7 @@ export default translate([], { wait: true })(withTracker(() => {
const userSubsBounds = SiteSettings.findOne({ name: 'subs-public-union-bounds' });
return {
userSubs: userSubs ? userSubs.value : null,
- userSubsBounds: userSubs ? userSubsBounds.value : null,
+ userSubsBounds: userSubsBounds ? userSubsBounds.value : null,
subsready: settingsSubs.ready()
};
})(SubscriptionsMap));
From 2a6d8bea7841fb5551196110c4e0dd611fe42ca0 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Fri, 16 Feb 2018 20:40:41 +0100
Subject: [PATCH 050/330] False positives only for active fires
---
.../api/ActiveFires/server/publications.js | 34 ++++++++++++++++++-
imports/ui/components/Maps/SubsUnion/Unify.js | 2 +-
imports/ui/pages/FiresMap/FiresMap.js | 23 ++++++-------
3 files changed, 45 insertions(+), 14 deletions(-)
diff --git a/imports/api/ActiveFires/server/publications.js b/imports/api/ActiveFires/server/publications.js
index 278d937..d4b389a 100644
--- a/imports/api/ActiveFires/server/publications.js
+++ b/imports/api/ActiveFires/server/publications.js
@@ -4,7 +4,10 @@
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
+import L from 'leaflet-headless';
import { NumberBetween } from '/imports/modules/server/other-checks';
+import FalsePositives from '/imports/api/FalsePositives/FalsePositives';
+import calcUnion from '/imports/ui/components/Maps/SubsUnion/Unify';
import ActiveFires from '../ActiveFires';
const counter = new Counter('countActiveFires', ActiveFires.find({}));
@@ -13,6 +16,27 @@ Meteor.publish('activefirestotal', function total() {
return counter;
});
+const falsePositives = (fires) => {
+ const falsePos = FalsePositives.find({
+ geo: {
+ $geoWithin: {
+ $geometry: fires.geometry
+ }
+ }
+ }, {
+ fields: {
+ geo: 1,
+ // type: 1,
+ // when: 1,
+ fireId: 1
+ }
+ });
+
+ /* console.log(`False positive total: ${falsePos.count()}`);
+ * console.log(`False positives: ${JSON.stringify(falsePos.fetch())}`); */
+ return falsePos;
+};
+
const activefires = (northEastLng, northEastLat, southWestLng, southWestLat) => {
const fires = ActiveFires.find({
ourid: {
@@ -31,8 +55,16 @@ const activefires = (northEastLng, northEastLat, southWestLng, southWestLat) =>
scan: 1
}
});
+
+ const group = new L.FeatureGroup();
+ const remap = fires.fetch().map(function remap(doc) {
+ return { location: { lat: doc.lat, lon: doc.lon }, distance: doc.scan };
+ });
+ const result = calcUnion(remap, group, sub => sub);
+ const falsePos = falsePositives(result[0]);
+ // console.log(JSON.stringify(result));
// console.log(`Fires total: ${fires.count()}`);
- return fires;
+ return [fires, falsePos];
};
Meteor.publish('activefiresmyloc', function activeInMyLoc(northEastLng, northEastLat, southWestLng, southWestLat) {
diff --git a/imports/ui/components/Maps/SubsUnion/Unify.js b/imports/ui/components/Maps/SubsUnion/Unify.js
index 68f602a..46586b7 100644
--- a/imports/ui/components/Maps/SubsUnion/Unify.js
+++ b/imports/ui/components/Maps/SubsUnion/Unify.js
@@ -35,7 +35,7 @@ const calcUnion = (subs, group, decorated) => {
const circle = LGeo.circle([dsub.location.lat, dsub.location.lon], dsub.distance * 1000, copts);
circle.addTo(unionGroup);
} else {
- console.error(`Wrong subscription ${JSON.stringify(osub)}`);
+ console.info(`Wrong subscription ${JSON.stringify(osub)}`);
}
} catch (e) {
console.error(e, `Wrong subscription trying to make union ${JSON.stringify(osub)}`);
diff --git a/imports/ui/pages/FiresMap/FiresMap.js b/imports/ui/pages/FiresMap/FiresMap.js
index 3319841..8520068 100644
--- a/imports/ui/pages/FiresMap/FiresMap.js
+++ b/imports/ui/pages/FiresMap/FiresMap.js
@@ -185,7 +185,7 @@ class FiresMap extends React.Component {
console.log(`Rendering ${this.props.loading ? 'loading' : 'LOADED'} map ${this.props.activefires.length + this.props.firealerts.length} of ${this.props.activefirestotal} total. False positives: ${this.props.falsePositives.length}. Reactive ${this.state.viewport.zoom >= MAXZOOMREACTIVE}`);
const title = `${t('AppName')}: ${t('Fuegos activos')}`;
if (Meteor.isDevelopment) {
- console.log(`False positives total: ${this.props.falsePositivesTotal}`);
+ console.log(`False positives total: ${this.props.falsePositives.length}`);
}
return (
/* Large number of markers:
@@ -321,7 +321,6 @@ FiresMap.propTypes = {
activefires: PropTypes.arrayOf(PropTypes.object).isRequired,
firealerts: PropTypes.arrayOf(PropTypes.object).isRequired,
falsePositives: PropTypes.arrayOf(PropTypes.object).isRequired,
- falsePositivesTotal: PropTypes.number.isRequired,
lastCheck: PropTypes.instanceOf(Date),
activefirestotal: PropTypes.number.isRequired,
center: PropTypes.arrayOf(PropTypes.number),
@@ -370,24 +369,25 @@ export default translate([], { wait: true })(withTracker(() => {
mapSize.get()[1].lng,
mapSize.get()[1].lat
);
- Meteor.subscribe(
- 'falsePositivesMyloc',
- mapSize.get()[0].lng,
- mapSize.get()[0].lat,
- mapSize.get()[1].lng,
- mapSize.get()[1].lat
- );
}
});
Meteor.subscribe('activefirestotal');
- Meteor.subscribe('falsePositivesTotal');
const settingsSubs = Meteor.subscribe('settings');
const lastCheck = SiteSettings.findOne({ name: 'last-fire-check' });
const userSubs = SiteSettings.findOne({ name: 'subs-public-union' });
const userSubsBounds = SiteSettings.findOne({ name: 'subs-public-union-bounds' });
const fireAlerts = FireAlertsCollection.find().fetch();
- const falsePositives = FalsePositivesCollection.find().fetch();
+ const falsePositives = FalsePositivesCollection.find().fetch().map((odoc) => {
+ const doc = odoc;
+ const geo = doc.geo;
+ doc.lat = geo.coordinates[1];
+ doc.lon = geo.coordinates[0];
+ doc._id = doc.fireId;
+ doc.id = doc.fireId;
+ delete doc.geo;
+ return doc;
+ });
return {
loading: !subscription ? true : !(subscription.ready() && settingsSubs.ready() && alertSubscription.ready()),
userSubs: userSubs ? userSubs.value : null,
@@ -396,7 +396,6 @@ export default translate([], { wait: true })(withTracker(() => {
// Not reactive query depending on zoom level
activefires: ActiveFiresCollection.find({}, { reactive: zoom.get() >= MAXZOOMREACTIVE }).fetch(),
activefirestotal: Counter.get('countActiveFires') + fireAlerts.length,
- falsePositivesTotal: Counter.get('countFalsePositives') + fireAlerts.length,
firealerts: fireAlerts,
falsePositives,
lastCheck: lastCheck ? lastCheck.value : null,
From 824f66df038f1427f06b8bf519401e96b61df63d Mon Sep 17 00:00:00 2001
From: vjrj
Date: Sat, 17 Feb 2018 08:26:46 +0100
Subject: [PATCH 051/330] import L
---
imports/ui/components/Maps/SubsUnion/SubsUnion.js | 2 +-
imports/ui/pages/FiresMap/FiresMap.js | 3 ++-
imports/ui/pages/Subscriptions/SubscriptionsMap.js | 3 ++-
3 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/imports/ui/components/Maps/SubsUnion/SubsUnion.js b/imports/ui/components/Maps/SubsUnion/SubsUnion.js
index ab28b21..dab127a 100644
--- a/imports/ui/components/Maps/SubsUnion/SubsUnion.js
+++ b/imports/ui/components/Maps/SubsUnion/SubsUnion.js
@@ -1,8 +1,8 @@
/* eslint-disable react/jsx-indent-props */
/* eslint-disable import/no-absolute-path */
/* eslint-disable import/no-absolute-path */
-/* global L */
+import L from 'leaflet';
import calcUnion from '/imports/ui/components/Maps/SubsUnion/Unify';
const subsUnion = (union, options) => {
diff --git a/imports/ui/pages/FiresMap/FiresMap.js b/imports/ui/pages/FiresMap/FiresMap.js
index 8520068..caf011e 100644
--- a/imports/ui/pages/FiresMap/FiresMap.js
+++ b/imports/ui/pages/FiresMap/FiresMap.js
@@ -1,4 +1,4 @@
-/* global L Counter */
+/* global Counter */
/* eslint-disable import/no-absolute-path */
/* eslint-disable react/jsx-indent-props */
/* eslint-disable react/jsx-indent */
@@ -14,6 +14,7 @@ import { Map } from 'react-leaflet';
import Control from 'react-leaflet-control';
import _ from 'lodash';
import store from 'store';
+import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
import 'leaflet-graphicscale/dist/Leaflet.GraphicScale.min.css';
import 'leaflet-graphicscale/dist/Leaflet.GraphicScale.min.js';
diff --git a/imports/ui/pages/Subscriptions/SubscriptionsMap.js b/imports/ui/pages/Subscriptions/SubscriptionsMap.js
index 69f5bdf..60bb37f 100644
--- a/imports/ui/pages/Subscriptions/SubscriptionsMap.js
+++ b/imports/ui/pages/Subscriptions/SubscriptionsMap.js
@@ -1,7 +1,7 @@
-/* global L */
/* eslint-disable import/no-absolute-path */
/* eslint-disable react/jsx-indent-props */
/* eslint-disable react/jsx-indent */
+
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { Button, ButtonGroup, Row, Col } from 'react-bootstrap';
@@ -10,6 +10,7 @@ import { withTracker } from 'meteor/react-meteor-data';
import { Trans, translate } from 'react-i18next';
import { Map } from 'react-leaflet';
import { Helmet } from 'react-helmet';
+import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
import 'leaflet-graphicscale/dist/Leaflet.GraphicScale.min.css';
import 'leaflet-graphicscale/dist/Leaflet.GraphicScale.min.js';
From 237269f76123c6fd9216b43e94376f478c31f311 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Sat, 17 Feb 2018 08:43:01 +0100
Subject: [PATCH 052/330] Fix some FireMaps data retrieving
---
.../api/ActiveFires/server/publications.js | 30 +++++++++++--------
imports/ui/pages/FiresMap/FiresMap.js | 6 ++--
2 files changed, 22 insertions(+), 14 deletions(-)
diff --git a/imports/api/ActiveFires/server/publications.js b/imports/api/ActiveFires/server/publications.js
index d4b389a..b0b2329 100644
--- a/imports/api/ActiveFires/server/publications.js
+++ b/imports/api/ActiveFires/server/publications.js
@@ -32,12 +32,12 @@ const falsePositives = (fires) => {
}
});
- /* console.log(`False positive total: ${falsePos.count()}`);
- * console.log(`False positives: ${JSON.stringify(falsePos.fetch())}`); */
+ /* console.log(`False positive total: ${falsePos.count()}`);
+ console.log(`False positives: ${JSON.stringify(falsePos.fetch())}`); */
return falsePos;
};
-const activefires = (northEastLng, northEastLat, southWestLng, southWestLat) => {
+const activefires = (northEastLng, northEastLat, southWestLng, southWestLat, withMarks) => {
const fires = ActiveFires.find({
ourid: {
$geoWithin: {
@@ -56,23 +56,29 @@ const activefires = (northEastLng, northEastLat, southWestLng, southWestLat) =>
}
});
- const group = new L.FeatureGroup();
- const remap = fires.fetch().map(function remap(doc) {
- return { location: { lat: doc.lat, lon: doc.lon }, distance: doc.scan };
- });
- const result = calcUnion(remap, group, sub => sub);
- const falsePos = falsePositives(result[0]);
// console.log(JSON.stringify(result));
// console.log(`Fires total: ${fires.count()}`);
- return [fires, falsePos];
+
+ if (withMarks && fires.fetch().length > 0) {
+ const group = new L.FeatureGroup();
+ const remap = fires.fetch().map(function remap(doc) {
+ return { location: { lat: doc.lat, lon: doc.lon }, distance: doc.scan };
+ });
+ const result = calcUnion(remap, group, sub => sub);
+ const falsePos = falsePositives(result[0]);
+ return [fires, falsePos];
+ }
+
+ return fires;
};
-Meteor.publish('activefiresmyloc', function activeInMyLoc(northEastLng, northEastLat, southWestLng, southWestLat) {
+Meteor.publish('activefiresmyloc', function activeInMyLoc(northEastLng, northEastLat, southWestLng, southWestLat, withMarks) {
// latitude -90 and 90 and the longitude between -180 and 180
check(northEastLng, NumberBetween(-180, 180));
check(southWestLat, NumberBetween(-90, 90));
check(southWestLng, NumberBetween(-180, 180));
check(northEastLat, NumberBetween(-90, 90));
+ check(withMarks, Boolean);
- return activefires(northEastLng, northEastLat, southWestLng, southWestLat);
+ return activefires(northEastLng, northEastLat, southWestLng, southWestLat, withMarks);
});
diff --git a/imports/ui/pages/FiresMap/FiresMap.js b/imports/ui/pages/FiresMap/FiresMap.js
index caf011e..c1b3b7b 100644
--- a/imports/ui/pages/FiresMap/FiresMap.js
+++ b/imports/ui/pages/FiresMap/FiresMap.js
@@ -146,6 +146,7 @@ class FiresMap extends React.Component {
useMarkers(use) {
this.setState({ useMarkers: use });
store.set('firesmap_marks', use);
+ marks.set(use);
}
addScale(map) {
@@ -183,7 +184,7 @@ class FiresMap extends React.Component {
render() {
const { t } = this.props;
- console.log(`Rendering ${this.props.loading ? 'loading' : 'LOADED'} map ${this.props.activefires.length + this.props.firealerts.length} of ${this.props.activefirestotal} total. False positives: ${this.props.falsePositives.length}. Reactive ${this.state.viewport.zoom >= MAXZOOMREACTIVE}`);
+ console.log(`Rendering ${this.props.loading ? 'loading' : 'LOADED'}, zoom ${this.state.viewport.zoom}, map ${this.props.activefires.length + this.props.firealerts.length} of ${this.props.activefirestotal} total. False positives: ${this.props.falsePositives.length}. Reactive ${this.state.viewport.zoom >= MAXZOOMREACTIVE}`);
const title = `${t('AppName')}: ${t('Fuegos activos')}`;
if (Meteor.isDevelopment) {
console.log(`False positives total: ${this.props.falsePositives.length}`);
@@ -361,7 +362,8 @@ export default translate([], { wait: true })(withTracker(() => {
mapSize.get()[0].lng,
mapSize.get()[0].lat,
mapSize.get()[1].lng,
- mapSize.get()[1].lat
+ mapSize.get()[1].lat,
+ marks.get() && zoom.get() >= MAXZOOM
);
alertSubscription = Meteor.subscribe(
'fireAlerts',
From 53ab69555edfd13ddb653ecbf6e7b1c21ab8d9b4 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Sat, 17 Feb 2018 08:48:04 +0100
Subject: [PATCH 053/330] Don't query again if zoom in
---
imports/ui/pages/FiresMap/FiresMap.js | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/imports/ui/pages/FiresMap/FiresMap.js b/imports/ui/pages/FiresMap/FiresMap.js
index c1b3b7b..7076c60 100644
--- a/imports/ui/pages/FiresMap/FiresMap.js
+++ b/imports/ui/pages/FiresMap/FiresMap.js
@@ -127,9 +127,8 @@ class FiresMap extends React.Component {
mapSize.set([bounds.getNorthEast(), bounds.getSouthWest()]);
store.set('firesmap_center', viewport.center);
store.set('firesmap_zoom', viewport.zoom);
- if (viewport.center === this.state.viewport.center &&
- viewport.zoom === this.state.viewport.zoom) {
- // Do nothing, in same point
+ if (viewport.zoom >= this.state.viewport.zoom) {
+ if (Meteor.isDevelopment) console.log('Don\'t query we are in the same point');
return;
}
zoom.set(viewport.zoom);
From f5d105fbc0543205dc2d7885a199e45002d3b5e1 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Sat, 17 Feb 2018 08:52:50 +0100
Subject: [PATCH 054/330] Remove log
---
imports/startup/server/prerender.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/imports/startup/server/prerender.js b/imports/startup/server/prerender.js
index 4942165..dedee0b 100644
--- a/imports/startup/server/prerender.js
+++ b/imports/startup/server/prerender.js
@@ -42,5 +42,5 @@ Meteor.startup(() => {
WebApp.rawConnectHandlers.use(prerenderIO);
- console.log('\nprerender service:', settings);
+ // console.log('\nprerender service:', settings);
});
From 79222eb97833eab833b6810890ba5ad86d118553 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Sat, 17 Feb 2018 09:30:44 +0100
Subject: [PATCH 055/330] Removed loading in FireMap. Improve data retrieving
---
imports/ui/pages/FiresMap/FiresMap.js | 11 +++--------
1 file changed, 3 insertions(+), 8 deletions(-)
diff --git a/imports/ui/pages/FiresMap/FiresMap.js b/imports/ui/pages/FiresMap/FiresMap.js
index 7076c60..afd5dcd 100644
--- a/imports/ui/pages/FiresMap/FiresMap.js
+++ b/imports/ui/pages/FiresMap/FiresMap.js
@@ -25,7 +25,6 @@ import FireList from '/imports/ui/components/Maps/FireList';
import subsUnion from '/imports/ui/components/Maps/SubsUnion/SubsUnion';
import DefMapLayers from '/imports/ui/components/Maps/DefMapLayers';
import FromNow from '/imports/ui/components/FromNow/FromNow';
-import Loading from '/imports/ui/components/Loading/Loading';
import ActiveFiresCollection from '/imports/api/ActiveFires/ActiveFires';
import FireAlertsCollection from '/imports/api/FireAlerts/FireAlerts';
import FalsePositivesCollection from '/imports/api/FalsePositives/FalsePositives';
@@ -127,13 +126,13 @@ class FiresMap extends React.Component {
mapSize.set([bounds.getNorthEast(), bounds.getSouthWest()]);
store.set('firesmap_center', viewport.center);
store.set('firesmap_zoom', viewport.zoom);
- if (viewport.zoom >= this.state.viewport.zoom) {
+ if (viewport.zoom > this.state.viewport.zoom) {
+ this.state.viewport = viewport;
if (Meteor.isDevelopment) console.log('Don\'t query we are in the same point');
return;
}
zoom.set(viewport.zoom);
center.set(viewport.center);
- // this.setState({ viewport });
this.state.viewport = viewport;
}
}
@@ -185,6 +184,7 @@ class FiresMap extends React.Component {
const { t } = this.props;
console.log(`Rendering ${this.props.loading ? 'loading' : 'LOADED'}, zoom ${this.state.viewport.zoom}, map ${this.props.activefires.length + this.props.firealerts.length} of ${this.props.activefirestotal} total. False positives: ${this.props.falsePositives.length}. Reactive ${this.state.viewport.zoom >= MAXZOOMREACTIVE}`);
const title = `${t('AppName')}: ${t('Fuegos activos')}`;
+
if (Meteor.isDevelopment) {
console.log(`False positives total: ${this.props.falsePositives.length}`);
}
@@ -199,11 +199,6 @@ class FiresMap extends React.Component {
{title}
}
- {this.props.loading || !this.props.subsready ?
-
-
-
- : ''}
Fuegos activos
From 308ad08e742e209773c6c432f2a356912449e20e Mon Sep 17 00:00:00 2001
From: vjrj
Date: Sat, 17 Feb 2018 09:41:21 +0100
Subject: [PATCH 056/330] Tooltip in industries
---
imports/ui/components/Maps/FireIconMark.js | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/imports/ui/components/Maps/FireIconMark.js b/imports/ui/components/Maps/FireIconMark.js
index 5082edf..5e8af68 100644
--- a/imports/ui/components/Maps/FireIconMark.js
+++ b/imports/ui/components/Maps/FireIconMark.js
@@ -1,6 +1,6 @@
/* eslint-disable import/no-absolute-path */
-import React from 'react';
-import { CircleMarker, Marker } from 'react-leaflet';
+import React, { Fragment } from 'react';
+import { CircleMarker, Marker, Tooltip } from 'react-leaflet';
import PropTypes from 'prop-types';
import { fireIcon, nFireIcon, industryIcon } from '/imports/ui/components/Maps/Icons';
import { translate } from 'react-i18next';
@@ -23,6 +23,7 @@ const FireIconMark = ({
}
{ falsePositives &&
+ {t('Es una industria')}
{ /* disabled because was a past fire (and can be marked multiple times) */ false && }
}
From 5c7380a1996b0071e3294c161317b5f0598f9f63 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Sun, 18 Feb 2018 13:11:40 +0100
Subject: [PATCH 057/330] Fix error message
---
imports/ui/pages/Profile/Profile.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/imports/ui/pages/Profile/Profile.js b/imports/ui/pages/Profile/Profile.js
index 3f3dbb2..cb58761 100644
--- a/imports/ui/pages/Profile/Profile.js
+++ b/imports/ui/pages/Profile/Profile.js
@@ -25,6 +25,7 @@ class Profile extends React.Component {
this.t = this.props.t;
this.getUserType = this.getUserType.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
+
this.renderOAuthUser = this.renderOAuthUser.bind(this);
this.renderPasswordUser = this.renderPasswordUser.bind(this);
this.renderProfileForm = this.renderProfileForm.bind(this);
@@ -67,7 +68,7 @@ class Profile extends React.Component {
required: this.t('¿Cuál es tu apellido?')
},
emailAddress: {
- required: this.t('Necesitamos una contraseña aquí.'),
+ required: this.t('Necesitamos un correo aquí.'),
email: this.t('¿Es correcto este correo?')
},
currentPassword: {
From ba32c4fd0dd89ba7a8e51647ee563fe9852eb771 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Sun, 18 Feb 2018 17:00:54 +0100
Subject: [PATCH 058/330] Feedback button
---
imports/startup/server/feedback.js | 25 ++++
imports/startup/server/index.js | 1 +
imports/ui/components/Feedback/Feedback.js | 120 +++++++++++++++++++
imports/ui/components/Feedback/Feedback.scss | 71 +++++++++++
imports/ui/layouts/App/App.js | 4 +-
public/locales/en/common.json | 10 +-
public/locales/es/common.json | 10 +-
7 files changed, 238 insertions(+), 3 deletions(-)
create mode 100644 imports/startup/server/feedback.js
create mode 100644 imports/ui/components/Feedback/Feedback.js
create mode 100644 imports/ui/components/Feedback/Feedback.scss
diff --git a/imports/startup/server/feedback.js b/imports/startup/server/feedback.js
new file mode 100644
index 0000000..98ebaca
--- /dev/null
+++ b/imports/startup/server/feedback.js
@@ -0,0 +1,25 @@
+/* eslint-disable prefer-arrow-callback */
+/* eslint-disable import/no-absolute-path */
+
+import { Meteor } from 'meteor/meteor';
+import i18n from 'i18next';
+import { check } from 'meteor/check';
+import sendEmail from '/imports/startup/server/email';
+
+Meteor.methods({
+ 'send-feedback': function sendFeedback(email, feedback) {
+ check(email, String);
+ check(feedback, String);
+ const appName = i18n.t('AppName');
+ sendEmail({
+ to: 'info@comunes.org',
+ from: `${appName} `,
+ subject: `Feedback de ${email}!`,
+ sendAt: new Date(),
+ text: `Feedback de ${email}\n\n${feedback}`,
+ html: `${feedback}
`,
+ template: '{{appName}}{{{subject}}} {{{html}}}',
+ appName
+ }, true);
+ }
+});
diff --git a/imports/startup/server/index.js b/imports/startup/server/index.js
index 6db3588..101d249 100644
--- a/imports/startup/server/index.js
+++ b/imports/startup/server/index.js
@@ -12,3 +12,4 @@ import '../common/comments';
import './sitemaps';
import './subsUnion';
import './prerender';
+import './feedback';
diff --git a/imports/ui/components/Feedback/Feedback.js b/imports/ui/components/Feedback/Feedback.js
new file mode 100644
index 0000000..af2f845
--- /dev/null
+++ b/imports/ui/components/Feedback/Feedback.js
@@ -0,0 +1,120 @@
+/* eslint-disable react/jsx-indent-props */
+/* eslint-disable import/no-absolute-path */
+/* eslint-disable import/no-absolute-path */
+
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { Meteor } from 'meteor/meteor';
+import { translate } from 'react-i18next';
+import { FormGroup, Button, FormControl } from 'react-bootstrap';
+import { Bert } from 'meteor/themeteorchef:bert';
+import validate from '../../../modules/validate';
+
+import './Feedback.scss';
+
+class Feedback extends Component {
+ constructor(props) {
+ super(props);
+ this.t = props.t;
+ this.handleSubmit = this.handleSubmit.bind(this);
+ this.onTabClick = this.onTabClick.bind(this);
+ }
+
+ componentDidMount() {
+ const component = this;
+
+ validate(component.form, {
+ rules: {
+ email: {
+ required: true,
+ email: true
+ },
+ feedbackText: {
+ required: true
+ }
+ },
+ messages: {
+ feedbackText: {
+ required: this.t('Por favor, escribe aquí tu feedback...')
+ },
+ email: {
+ required: this.t('Tu correo'),
+ email: this.t('¿Es correcto este correo?')
+ }
+ },
+ submitHandler() { component.handleSubmit(); }
+ });
+ }
+
+ onTabClick() {
+ $('#feedback-form').toggle('slide');
+ }
+
+ handleSubmit() {
+ const email = this.email.value.trim();
+ const feedbackText = this.feedbackText.value.trim();
+
+ Meteor.call('send-feedback', email, feedbackText, (error) => {
+ if (error) {
+ Bert.alert(error.reason, 'danger');
+ } else {
+ this.form.reset();
+ Bert.alert('Feedback recibido, gracias...', 'success');
+ this.onTabClick();
+ }
+ });
+ }
+
+ render() {
+ return (
+
+
(this.formdiv = formdiv)} style={{ display: 'none' }} className="card">
+
+
+
{ this.onTabClick(event); }}
+ >
+ {this.t('Feedback')}
+
+
+ );
+ }
+}
+
+Feedback.propTypes = {
+ t: PropTypes.func.isRequired
+};
+
+export default translate([], { wait: true })(Feedback);
diff --git a/imports/ui/components/Feedback/Feedback.scss b/imports/ui/components/Feedback/Feedback.scss
new file mode 100644
index 0000000..5e7b5fb
--- /dev/null
+++ b/imports/ui/components/Feedback/Feedback.scss
@@ -0,0 +1,71 @@
+/* From: https://github.com/spektom/bootstrap-feedback-form */
+@import '../../stylesheets/mixins';
+
+#feedback {
+ position: fixed;
+ z-index: 1000;
+ left: 0;
+ bottom: 0;
+ height: 350px;
+ margin-left: -3px;
+ margin-bottom: 10px;
+ font-size: 12px;
+}
+
+@include breakpoint(tablet) {
+ #feedback {
+ height: 400px;
+ }
+}
+
+#feedback-form {
+ float: left;
+ width: 400px;
+ height: 100%;
+ z-index: 1000;
+ padding-left: 5px;
+ padding-right: 10px;
+ background-clip: padding-box;
+ border: 1px solid rgba(0,0,0,.2);
+ border-radius: 0px;
+ box-shadow: 0 5px 10px rgba(0,0,0,.2);
+}
+
+@include breakpoint(mobile) {
+ #feedback-form {
+ width: 270px;
+ }
+}
+
+#feedback-tab {
+ float: right;
+ color: #fff;
+ font-size: 12px;
+ cursor: pointer;
+ text-align: center;
+ width: 80px;
+ height: 24px;
+ background-color: rgba(0,0,0,0.5);
+ margin-top: 60px;
+ margin-left: -28px;
+ padding-top: 3px;
+ -webkit-border-top-left-radius: 3px;
+ -webkit-border-top-right-radius: 3px;
+ -moz-border-radius-topleft: 3px;
+ -moz-border-radius-topright: 3px;
+ border-top-left-radius: 3px;
+ border-top-right-radius: 3px;
+ -webkit-transform: rotate(90deg);
+ -moz-transform: rotate(90deg);
+ -ms-transform: rotate(90deg);
+ -o-transform: rotate(90deg);
+ transform: rotate(90deg);
+}
+
+#feedback-tab:hover {
+ background-color: rgba(0,0,0,0.4);
+}
+
+#feedback-form textarea {
+ resize: none;
+}
diff --git a/imports/ui/layouts/App/App.js b/imports/ui/layouts/App/App.js
index e80d8cc..ae72672 100644
--- a/imports/ui/layouts/App/App.js
+++ b/imports/ui/layouts/App/App.js
@@ -42,12 +42,13 @@ import NotFound from '../../pages/NotFound/NotFound';
import FiresMap from '../../pages/FiresMap/FiresMap';
import Fires from '../../pages/Fires/Fires';
import Sandbox from '../../pages/Sandbox/Sandbox';
-import Footer from '../../components/Footer/Footer';
import Terms from '../../pages/Terms/Terms';
import Privacy from '../../pages/Privacy/Privacy';
import License from '../../pages/License/License';
import Credits from '../../pages/Credits/Credits';
import ZonesMap from '../../pages/ZonesMap/ZonesMap';
+import Footer from '../../components/Footer/Footer';
+import Feedback from '../../components/Feedback/Feedback';
import ReSendEmail from '../../components/ReSendEmail/ReSendEmail';
import ErrorBoundary from '../../components/ErrorBoundary/ErrorBoundary';
import history from '../../components/History/History';
@@ -138,6 +139,7 @@ const App = props => (
+
{props.i18nReady.get() &&
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index fd4fcd3..0ea5af5 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -190,5 +190,13 @@
"Choose a type",
"Puedes participar en las traducciones":
"You can help with the translations",
- "Aceptar": "Accept"
+ "Aceptar": "Accept",
+ "Por favor, escribe aquí tu feedback...":
+ "Please write your feedback here...",
+ "Feedback":
+ "Feedback",
+ "Enviar":
+ "Send",
+ "Tu correo":
+ "Your e-mail"
}
diff --git a/public/locales/es/common.json b/public/locales/es/common.json
index 49ae9e0..c1beda9 100644
--- a/public/locales/es/common.json
+++ b/public/locales/es/common.json
@@ -277,5 +277,13 @@
"Elige un tipo",
"Puedes participar en las traducciones":
"Puedes participar en las traducciones",
- "Aceptar": "Aceptar"
+ "Aceptar": "Aceptar",
+ "Por favor, escribe aquí tu feedback...":
+ "Por favor, escribe aquí tu feedback...",
+ "Feedback":
+ "Feedback",
+ "Enviar":
+ "Enviar",
+ "Tu correo":
+ "Tu correo"
}
From 169a955b0bb76d696f092264cc8af9afc4a77320 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Sun, 18 Feb 2018 17:33:46 +0100
Subject: [PATCH 059/330] Simple font update
---
fonts.json | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/fonts.json b/fonts.json
index ba2e995..f543f21 100644
--- a/fonts.json
+++ b/fonts.json
@@ -1,10 +1,9 @@
{
"extensions": [ "ttf", "woff", "woff2" ],
"map": {
- "node_modules/simple-line-icons/fonts/Simple-Line-Icons.woff": "fonts/Simple-Line-Icons.woff",
- "node_modules/simple-line-icons/fonts/Simple-Line-Icons.woff": "fonts/Simple-Line-Icons.woff?v=2.4.0",
- "node_modules/simple-line-icons/fonts/Simple-Line-Icons.woff2": "fonts/Simple-Line-Icons.woff2?v=2.4.0",
- "node_modules/simple-line-icons/fonts/Simple-Line-Icons.ttf": "fonts/Simple-Line-Icons.ttf?v=2.4.0"
+ "node_modules/simple-line-icons/fonts/Simple-Line-Icons.woff": "fonts/Simple-Line-Icons.woff?v=2.4.1",
+ "node_modules/simple-line-icons/fonts/Simple-Line-Icons.woff2": "fonts/Simple-Line-Icons.woff2?v=2.4.1",
+ "node_modules/simple-line-icons/fonts/Simple-Line-Icons.ttf": "fonts/Simple-Line-Icons.ttf?v=2.4.1"
},
"verbose": false
}
From 9115063cdd4f333168e777c0f59160ea5d09c680 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Sun, 18 Feb 2018 17:34:13 +0100
Subject: [PATCH 060/330] Update carousel swipe
---
imports/ui/pages/Index/Index.js | 8 +++-----
package-lock.json | 8 ++++----
package.json | 2 +-
3 files changed, 8 insertions(+), 10 deletions(-)
diff --git a/imports/ui/pages/Index/Index.js b/imports/ui/pages/Index/Index.js
index d353423..27c107a 100644
--- a/imports/ui/pages/Index/Index.js
+++ b/imports/ui/pages/Index/Index.js
@@ -12,7 +12,7 @@ import PropTypes from 'prop-types';
import ReactResizeDetector from 'react-resize-detector';
import _ from 'lodash';
import 'html5-device-mockups/dist/device-mockups.min.css';
-import 'bootstrap-carousel-swipe-haven/carousel-swipe';
+import 'bootstrap-carousel-swipe/carousel-swipe';
import { isAnyMobile } from '/imports/ui/components/Utils/isMobile';
import SubscriptionEditor from '/imports/ui/components/SubscriptionEditor/SubscriptionEditor';
import SubscriptionsMap from '/imports/ui/pages/Subscriptions/SubscriptionsMap';
@@ -36,10 +36,8 @@ class Index extends Component {
}
componentDidMount() {
- const c = $('.carousel');
- if (c.carousel) {
- c.carousel();
- }
+ $('#carouselMainIndicators').carousel();
+ $('#carouselSndIndicators').carousel();
}
onResize() {
diff --git a/package-lock.json b/package-lock.json
index 7aeb4b8..44d1d4c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2153,10 +2153,10 @@
"hoek": "4.2.0"
}
},
- "bootstrap-carousel-swipe-haven": {
- "version": "0.0.7",
- "resolved": "https://registry.npmjs.org/bootstrap-carousel-swipe-haven/-/bootstrap-carousel-swipe-haven-0.0.7.tgz",
- "integrity": "sha512-Dd033dpeHuW35RQ/8v176aGaJZdr7VvzsGDb878ebU18D0J+v1aDj5MkDYurCEHGIBopbJUBxgO3SKt/jwL6lA=="
+ "bootstrap-carousel-swipe": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/bootstrap-carousel-swipe/-/bootstrap-carousel-swipe-0.0.6.tgz",
+ "integrity": "sha1-FTn+656kduHRDRwwnWWb0WJZ/wo="
},
"brace-expansion": {
"version": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz",
diff --git a/package.json b/package.json
index 32d4fbb..26191ba 100644
--- a/package.json
+++ b/package.json
@@ -12,7 +12,7 @@
"@turf/union": "^5.1.5",
"babel-runtime": "^6.26.0",
"bcrypt": "^1.0.3",
- "bootstrap-carousel-swipe-haven": "0.0.7",
+ "bootstrap-carousel-swipe": "0.0.6",
"commonmark": "^0.28.1",
"core-js": "^2.5.1",
"crypto-random-hex": "^1.0.0",
From 2c53428f8dffe3725f261d79dc418d58ee3d78e3 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Sun, 18 Feb 2018 17:37:39 +0100
Subject: [PATCH 061/330] Feedback only for auth users
---
imports/ui/layouts/App/App.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/imports/ui/layouts/App/App.js b/imports/ui/layouts/App/App.js
index ae72672..ba9d649 100644
--- a/imports/ui/layouts/App/App.js
+++ b/imports/ui/layouts/App/App.js
@@ -139,7 +139,7 @@ const App = props => (
-
+ { props.authenticated && }
{props.i18nReady.get() &&
From 458627f520fe64b1a2c09ddb7afa05fd321ad14f Mon Sep 17 00:00:00 2001
From: vjrj
Date: Sun, 18 Feb 2018 18:43:40 +0100
Subject: [PATCH 062/330] Fix reconnect translation issues
---
imports/ui/components/Reconnect/Reconnect.js | 29 +++++++++++++++++---
public/locales/en/common.json | 8 +++++-
public/locales/es/common.json | 8 +++++-
3 files changed, 39 insertions(+), 6 deletions(-)
diff --git a/imports/ui/components/Reconnect/Reconnect.js b/imports/ui/components/Reconnect/Reconnect.js
index 3fa35b4..c2fdb04 100644
--- a/imports/ui/components/Reconnect/Reconnect.js
+++ b/imports/ui/components/Reconnect/Reconnect.js
@@ -1,12 +1,33 @@
-import React from 'react';
+/* eslint-disable react/jsx-indent-props */
+/* eslint-disable import/no-absolute-path */
+
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
import { translate } from 'react-i18next';
import { Meteor } from 'meteor/meteor';
import { Tracker } from 'meteor/tracker';
import Blaze from 'meteor/gadicc:blaze-react-component';
-const Reconnect = props => (
-
-);
+class Reconnect extends Component {
+ render() {
+ const { t } = this.props;
+ return (
+
+ );
+ }
+}
+
+Reconnect.propTypes = {
+ t: PropTypes.func.isRequired
+};
+
+Reconnect.defaultProps = {
+};
export default translate([], { wait: true })(Reconnect);
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index 0ea5af5..fabdf90 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -198,5 +198,11 @@
"Enviar":
"Send",
"Tu correo":
- "Your e-mail"
+ "Your e-mail",
+ "Desconectado del servidor, reconectando en %delay% segundos.":
+ "Disconnected from server, trying to reconnect in %delay%s.",
+ "Desconectado del servidor, reconectando...":
+ "Disconnected from server, trying to reconnect...",
+ "Reintentar ahora":
+ "Retry now"
}
diff --git a/public/locales/es/common.json b/public/locales/es/common.json
index c1beda9..c05ab2f 100644
--- a/public/locales/es/common.json
+++ b/public/locales/es/common.json
@@ -285,5 +285,11 @@
"Enviar":
"Enviar",
"Tu correo":
- "Tu correo"
+ "Tu correo",
+ "Desconectado del servidor, reconectando en %delay% segundos.":
+ "Desconectado del servidor, reconectando en %delay% segundos.",
+ "Desconectado del servidor, reconectando...":
+ "Desconectado del servidor, reconectando...",
+ "Reintentar ahora":
+ "Reintentar ahora"
}
From 719ea435d41bea871db83c9cd4e21d1327513ed0 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Sun, 18 Feb 2018 20:21:26 +0100
Subject: [PATCH 063/330] Feedback not in home and email for non auth users
---
imports/ui/components/Feedback/Feedback.js | 11 ++++++++---
imports/ui/components/Utils/isMobile.js | 3 ++-
imports/ui/layouts/App/App.js | 3 ++-
public/locales/en/common.json | 4 +++-
public/locales/es/common.json | 4 +++-
5 files changed, 18 insertions(+), 7 deletions(-)
diff --git a/imports/ui/components/Feedback/Feedback.js b/imports/ui/components/Feedback/Feedback.js
index af2f845..43277e1 100644
--- a/imports/ui/components/Feedback/Feedback.js
+++ b/imports/ui/components/Feedback/Feedback.js
@@ -8,6 +8,7 @@ import { Meteor } from 'meteor/meteor';
import { translate } from 'react-i18next';
import { FormGroup, Button, FormControl } from 'react-bootstrap';
import { Bert } from 'meteor/themeteorchef:bert';
+import { withTracker } from 'meteor/react-meteor-data';
import validate from '../../../modules/validate';
import './Feedback.scss';
@@ -78,10 +79,11 @@ class Feedback extends Component {
(this.email = email)}
placeholder={this.t('Tu correo')}
+ disabled={this.props.authenticated}
+ defaultValue={this.props.authenticated ? Meteor.user().emails[0].address : ''}
type="email"
/>
@@ -114,7 +116,10 @@ class Feedback extends Component {
}
Feedback.propTypes = {
- t: PropTypes.func.isRequired
+ t: PropTypes.func.isRequired,
+ authenticated: PropTypes.bool.isRequired
};
-export default translate([], { wait: true })(Feedback);
+export default translate([], { wait: true })(withTracker(() => ({
+ authenticated: !!Meteor.userId()
+}))(Feedback));
diff --git a/imports/ui/components/Utils/isMobile.js b/imports/ui/components/Utils/isMobile.js
index 868ba97..4328a3d 100644
--- a/imports/ui/components/Utils/isMobile.js
+++ b/imports/ui/components/Utils/isMobile.js
@@ -1,7 +1,8 @@
import isMobile from 'ismobilejs';
+import { isHome } from './location';
// https://github.com/ReactTraining/react-router/issues/184
-export const isHomeAndMobile = (window.location.pathname === '/' && isMobile.any);
+export const isHomeAndMobile = isHome() && isMobile.any;
export const isNotHomeAndMobile = !isHomeAndMobile;
export const isAnyMobile = isMobile.any;
// https://stackoverflow.com/questions/18625321/detect-webkit-browser-in-javascript
diff --git a/imports/ui/layouts/App/App.js b/imports/ui/layouts/App/App.js
index ba9d649..89d427e 100644
--- a/imports/ui/layouts/App/App.js
+++ b/imports/ui/layouts/App/App.js
@@ -19,6 +19,7 @@ import '/imports/startup/client/ravenLogger';
import '/imports/startup/client/geolocation';
import '/imports/startup/client/piwik-start.js';
import 'simple-line-icons/css/simple-line-icons.css';
+import { isHome } from '/imports/ui/components/Utils/location';
import Reconnect from '../../components/Reconnect/Reconnect';
import Navigation from '../../components/Navigation/Navigation';
import Authenticated from '../../components/Authenticated/Authenticated';
@@ -139,7 +140,7 @@ const App = props => (
- { props.authenticated && }
+ { !isHome() && }
{props.i18nReady.get() &&
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index fabdf90..e51404e 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -204,5 +204,7 @@
"Desconectado del servidor, reconectando...":
"Disconnected from server, trying to reconnect...",
"Reintentar ahora":
- "Retry now"
+ "Retry now",
+ "Feedback recibido, gracias...":
+ "Feedback received, thanks..."
}
diff --git a/public/locales/es/common.json b/public/locales/es/common.json
index c05ab2f..dafe892 100644
--- a/public/locales/es/common.json
+++ b/public/locales/es/common.json
@@ -291,5 +291,7 @@
"Desconectado del servidor, reconectando...":
"Desconectado del servidor, reconectando...",
"Reintentar ahora":
- "Reintentar ahora"
+ "Reintentar ahora",
+ "Feedback recibido, gracias...":
+ "Feedback recibido, gracias..."
}
From 151c792fc6f4e3747b58f1d9102fae6e15b029c2 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Mon, 19 Feb 2018 09:40:40 +0100
Subject: [PATCH 064/330] Disabled feedback in production
---
imports/ui/components/Feedback/Feedback.js | 13 +++++++------
imports/ui/layouts/App/App.js | 9 ++++++---
2 files changed, 13 insertions(+), 9 deletions(-)
diff --git a/imports/ui/components/Feedback/Feedback.js b/imports/ui/components/Feedback/Feedback.js
index 43277e1..c6b8919 100644
--- a/imports/ui/components/Feedback/Feedback.js
+++ b/imports/ui/components/Feedback/Feedback.js
@@ -82,8 +82,8 @@ class Feedback extends Component {
className="form-control"
ref={email => (this.email = email)}
placeholder={this.t('Tu correo')}
- disabled={this.props.authenticated}
- defaultValue={this.props.authenticated ? Meteor.user().emails[0].address : ''}
+ disabled={this.props.emailVerified}
+ defaultValue={this.props.emailVerified ? this.props.emailAddress : ''}
type="email"
/>
@@ -117,9 +117,10 @@ class Feedback extends Component {
Feedback.propTypes = {
t: PropTypes.func.isRequired,
- authenticated: PropTypes.bool.isRequired
+ emailAddress: PropTypes.string,
+ emailVerified: PropTypes.bool.isRequired
};
-
-export default translate([], { wait: true })(withTracker(() => ({
- authenticated: !!Meteor.userId()
+export default translate([], { wait: true })(withTracker(props => ({
+ emailAddress: props.emailAddress,
+ emailVerified: props.emailVerified
}))(Feedback));
diff --git a/imports/ui/layouts/App/App.js b/imports/ui/layouts/App/App.js
index 89d427e..4089c65 100644
--- a/imports/ui/layouts/App/App.js
+++ b/imports/ui/layouts/App/App.js
@@ -140,8 +140,8 @@ const App = props => (
- { !isHome() && }
-
+ { Meteor.isDevelopment && !props.isHome && }
+
{props.i18nReady.get() &&
}
@@ -162,7 +162,8 @@ App.propTypes = {
i18nReady: PropTypes.object.isRequired,
userId: PropTypes.string,
emailAddress: PropTypes.string,
- emailVerified: PropTypes.bool.isRequired
+ emailVerified: PropTypes.bool.isRequired,
+ isHome: PropTypes.bool.isRequired
};
const getUserName = name => ({
@@ -178,6 +179,7 @@ export default withTracker(() => {
const name = user && user.profile && user.profile.name && getUserName(user.profile.name);
const emailAddress = user && user.emails && user.emails[0].address;
// console.log(`i18n ready?: ${i18nReady.get()}`);
+ console.log(`isHome?: ${isHome()}`);
return {
loading,
loggingIn,
@@ -187,6 +189,7 @@ export default withTracker(() => {
roles: !loading && Roles.getRolesForUser(userId),
userId,
emailAddress,
+ isHome: isHome(),
emailVerified: user && user.emails ? user && user.emails && user.emails[0].verified : true
};
})(App);
From 4b9d742b9b6e920ca7e767916dc6541f687c7050 Mon Sep 17 00:00:00 2001
From: marcos
Date: Mon, 19 Feb 2018 16:01:43 +0100
Subject: [PATCH 065/330] errata en radio
---
imports/ui/components/SubscriptionEditor/SubscriptionEditor.js | 2 +-
public/locales/en/common.json | 2 +-
public/locales/es/common.json | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/imports/ui/components/SubscriptionEditor/SubscriptionEditor.js b/imports/ui/components/SubscriptionEditor/SubscriptionEditor.js
index f391402..f846aca 100644
--- a/imports/ui/components/SubscriptionEditor/SubscriptionEditor.js
+++ b/imports/ui/components/SubscriptionEditor/SubscriptionEditor.js
@@ -58,7 +58,7 @@ class SubscriptionEditor extends React.Component {
zoom={this.state.zoom}
distance={doc.distance}
focusInput={focus}
- subsBtn={isEdit ? t('Actualizar') : t('Subscribirme a fuegos en este rádio')}
+ subsBtn={isEdit ? t('Actualizar') : t('Subscribirme a fuegos en este radio')}
onSubs={state => this.onSubs(state)}
/>
);
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index e51404e..def8ec6 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -78,7 +78,7 @@
"Nuevas notificaciones de {{app}}": "New notifications of {{app}}",
"Mis zonas": "My areas",
"Nueva zona": "New area",
- "Subscribirme a fuegos en este rádio": "Subscribe to fires in this radio",
+ "Subscribirme a fuegos en este radio": "Subscribe to fires in this radio",
"Créditos": "Credits",
"Escribe aquí un lugar": "Write here a place",
"Indícanos la posición de la zona a vigilar (por ej. tu pueblo, una calle, etc):": "Tell us the position of the area to be monitored (eg your town, a street, etc.):",
diff --git a/public/locales/es/common.json b/public/locales/es/common.json
index dafe892..c5e1efa 100644
--- a/public/locales/es/common.json
+++ b/public/locales/es/common.json
@@ -138,7 +138,7 @@
"Suscripción actualizada",
"Mis zonas": "Mis zonas",
"Nueva zona": "Nueva zona",
- "Subscribirme a fuegos en este rádio": "Subscribirme a fuegos en este rádio",
+ "Subscribirme a fuegos en este radio": "Subscribirme a fuegos en este radio",
"Créditos": "Créditos",
"Escribe aquí un lugar": "Escribe aquí un lugar",
"Indícanos la posición de la zona a vigilar (por ej. tu pueblo, una calle, etc):": "Indícanos la posición de la zona a vigilar (por ej. tu pueblo, una calle, etc):",
From 8b26accb788fdbeb88cefd43c7097e14c38e4201 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Mon, 19 Feb 2018 16:12:09 +0100
Subject: [PATCH 066/330] Minor changes in individual Fire map style/usability
---
imports/ui/pages/Fires/Fires.js | 3 ++-
imports/ui/pages/Fires/Fires.scss | 7 ++++---
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/imports/ui/pages/Fires/Fires.js b/imports/ui/pages/Fires/Fires.js
index a892181..662b234 100644
--- a/imports/ui/pages/Fires/Fires.js
+++ b/imports/ui/pages/Fires/Fires.js
@@ -100,7 +100,8 @@ class Fire extends React.Component {
center={[fire.lat, fire.lon]}
color="red"
fillColor="red"
- fillOpacity={0.1}
+ fillOpacity={0.0}
+ interactive={false}
radius={fire.scan ? fire.scan * 1000 : 300}
/>
diff --git a/imports/ui/pages/Fires/Fires.scss b/imports/ui/pages/Fires/Fires.scss
index a2577ec..213d9e8 100644
--- a/imports/ui/pages/Fires/Fires.scss
+++ b/imports/ui/pages/Fires/Fires.scss
@@ -2,7 +2,7 @@
@import '../../stylesheets/mixins';
.fire-leaflet-container {
- min-height: 30vh;
+ min-height: 40vh;
min-width: 40vw;
width: 50%;
margin: 0 0 10px 10px !important;
@@ -11,7 +11,8 @@
}
.ViewFire > h4.page-header {
- line-height: 1.3em;
+ line-height: 1.3em;
+ font-size: 1.5em;
}
@media (max-width: 768px) {
@@ -28,4 +29,4 @@
border-color: #fdfdfe;
padding: 10px;
border-radius: 3px; */
-}
\ No newline at end of file
+}
From 8af9bec79c9339f68eeed9f558f0cb9154af0a28 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Mon, 19 Feb 2018 16:31:05 +0100
Subject: [PATCH 067/330] Trying to fix fonts issue 'Failed to decode
downloaded' because of wrong path
---
fonts.json | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/fonts.json b/fonts.json
index f543f21..81a6ae1 100644
--- a/fonts.json
+++ b/fonts.json
@@ -1,9 +1,9 @@
{
"extensions": [ "ttf", "woff", "woff2" ],
"map": {
- "node_modules/simple-line-icons/fonts/Simple-Line-Icons.woff": "fonts/Simple-Line-Icons.woff?v=2.4.1",
- "node_modules/simple-line-icons/fonts/Simple-Line-Icons.woff2": "fonts/Simple-Line-Icons.woff2?v=2.4.1",
- "node_modules/simple-line-icons/fonts/Simple-Line-Icons.ttf": "fonts/Simple-Line-Icons.ttf?v=2.4.1"
+ "node_modules/simple-line-icons/fonts/Simple-Line-Icons.woff": "/fonts/Simple-Line-Icons.woff?v=2.4.1",
+ "node_modules/simple-line-icons/fonts/Simple-Line-Icons.woff2": "/fonts/Simple-Line-Icons.woff2?v=2.4.1",
+ "node_modules/simple-line-icons/fonts/Simple-Line-Icons.ttf": "/fonts/Simple-Line-Icons.ttf?v=2.4.1"
},
"verbose": false
}
From 34e0132c4673e524d1035470eea926c9a88ee599 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Mon, 19 Feb 2018 21:20:20 +0100
Subject: [PATCH 068/330] Doble check state in subscriptions
---
imports/ui/pages/Subscriptions/Subscriptions.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/imports/ui/pages/Subscriptions/Subscriptions.js b/imports/ui/pages/Subscriptions/Subscriptions.js
index f9b3cc6..78fbcad 100644
--- a/imports/ui/pages/Subscriptions/Subscriptions.js
+++ b/imports/ui/pages/Subscriptions/Subscriptions.js
@@ -20,8 +20,8 @@ class Subscriptions extends Component {
constructor(props) {
super(props);
this.t = props.t;
- if (props.location.state) {
- const received = props.location.state;
+ const received = props.location.state;
+ if (received && received.location && received.location.lat && received.location.lon && received.distance) {
props.history.push(`${this.props.match.url}/new`, {
location: received.location,
distance: received.distance,
From c962d12374b85c5ca273eb74e93463f6c199ef12 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Mon, 19 Feb 2018 21:20:34 +0100
Subject: [PATCH 069/330] Better notifications
---
private/email-templates/new-fire-en.html | 7 +++++++
private/email-templates/new-fire-en.txt | 4 ++++
private/email-templates/new-fire-es.html | 7 +++++++
private/email-templates/new-fire-es.txt | 4 ++++
4 files changed, 22 insertions(+)
diff --git a/private/email-templates/new-fire-en.html b/private/email-templates/new-fire-en.html
index 18b97a9..b0f7fcb 100644
--- a/private/email-templates/new-fire-en.html
+++ b/private/email-templates/new-fire-en.html
@@ -298,6 +298,13 @@
+ Is not it a forest fire? In the previous link you can tell us what kind of fire it is and help us improve our notifications.
+
+ You can also add comments on that link if you have additional information about this fire (how to access that area, if you know how the fire originated, etc).
+
+
+
+
Thanks!
{{applicationName}} Team
diff --git a/private/email-templates/new-fire-en.txt b/private/email-templates/new-fire-en.txt
index 61c9eb6..1b00d23 100644
--- a/private/email-templates/new-fire-en.txt
+++ b/private/email-templates/new-fire-en.txt
@@ -4,6 +4,10 @@ Hey, {{firstName}}!
[More information about this fire]({{fireUrl}})
+Isn't it a forest fire? In the previous link you can tell us what kind of fire it is and help us improve our notifications.
+
+You can also add comments on that link if you have additional information about this fire (how to access that area, if you know how the fire originated, etc).
+
Thanks!
{{applicationName}} Team
diff --git a/private/email-templates/new-fire-es.html b/private/email-templates/new-fire-es.html
index b4eafe5..e254a8a 100644
--- a/private/email-templates/new-fire-es.html
+++ b/private/email-templates/new-fire-es.html
@@ -298,6 +298,13 @@
+ ¿No es un fuego forestal? En el enlace anterior puedes indicarnos de que tipo de fuego se trata y ayudarnos así a mejorar nuestras notificaciones.
+
+ También puedes añadir comentarios en ese enlace si tienes información adicional sobre este fuego (cómo acceder a esa zona, si conoces cómo se originó el fuego, etc).
+
+
+
+
Gracias!
El equipo de {{applicationName}}
diff --git a/private/email-templates/new-fire-es.txt b/private/email-templates/new-fire-es.txt
index 15a4392..36bb3b9 100644
--- a/private/email-templates/new-fire-es.txt
+++ b/private/email-templates/new-fire-es.txt
@@ -4,6 +4,10 @@ Hola, {{firstName}}!
[Más información sobre este fuego]({{fireUrl}})
+¿No es un fuego forestal? En el enlace anterior puedes indicarnos de que tipo de fuego se trata y ayudarnos así a mejorar nuestras notificaciones.
+
+También puedes añadir comentarios en ese enlace si tienes información adicional sobre este fuego (cómo acceder a esa zona, si conoces cómo se originó el fuego, etc).
+
Gracias!
El equipo de {{applicationName}}
From f0bf995584ae1e2cb6fb191f55791e8282a34f91 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Tue, 20 Feb 2018 07:55:26 +0100
Subject: [PATCH 070/330] Fire page now shows also false positives
---
.../api/ActiveFires/server/publications.js | 32 ++-----------------
imports/api/FalsePositives/FalsePositives.js | 11 +++++++
.../api/FalsePositives/server/publications.js | 29 +++++++++++++++++
imports/api/Fires/server/publications.js | 6 ++--
imports/ui/pages/Fires/Fires.js | 22 ++++++++++++-
imports/ui/pages/FiresMap/FiresMap.js | 18 +++--------
public/locales/en/common.json | 4 ++-
public/locales/es/common.json | 4 ++-
8 files changed, 77 insertions(+), 49 deletions(-)
diff --git a/imports/api/ActiveFires/server/publications.js b/imports/api/ActiveFires/server/publications.js
index b0b2329..6fd66eb 100644
--- a/imports/api/ActiveFires/server/publications.js
+++ b/imports/api/ActiveFires/server/publications.js
@@ -4,10 +4,8 @@
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
-import L from 'leaflet-headless';
import { NumberBetween } from '/imports/modules/server/other-checks';
-import FalsePositives from '/imports/api/FalsePositives/FalsePositives';
-import calcUnion from '/imports/ui/components/Maps/SubsUnion/Unify';
+import { whichAreFalsePositives } from '/imports/api/FalsePositives/server/publications';
import ActiveFires from '../ActiveFires';
const counter = new Counter('countActiveFires', ActiveFires.find({}));
@@ -16,27 +14,6 @@ Meteor.publish('activefirestotal', function total() {
return counter;
});
-const falsePositives = (fires) => {
- const falsePos = FalsePositives.find({
- geo: {
- $geoWithin: {
- $geometry: fires.geometry
- }
- }
- }, {
- fields: {
- geo: 1,
- // type: 1,
- // when: 1,
- fireId: 1
- }
- });
-
- /* console.log(`False positive total: ${falsePos.count()}`);
- console.log(`False positives: ${JSON.stringify(falsePos.fetch())}`); */
- return falsePos;
-};
-
const activefires = (northEastLng, northEastLat, southWestLng, southWestLat, withMarks) => {
const fires = ActiveFires.find({
ourid: {
@@ -60,12 +37,7 @@ const activefires = (northEastLng, northEastLat, southWestLng, southWestLat, wit
// console.log(`Fires total: ${fires.count()}`);
if (withMarks && fires.fetch().length > 0) {
- const group = new L.FeatureGroup();
- const remap = fires.fetch().map(function remap(doc) {
- return { location: { lat: doc.lat, lon: doc.lon }, distance: doc.scan };
- });
- const result = calcUnion(remap, group, sub => sub);
- const falsePos = falsePositives(result[0]);
+ const falsePos = whichAreFalsePositives(fires);
return [fires, falsePos];
}
diff --git a/imports/api/FalsePositives/FalsePositives.js b/imports/api/FalsePositives/FalsePositives.js
index 592fe12..eead435 100644
--- a/imports/api/FalsePositives/FalsePositives.js
+++ b/imports/api/FalsePositives/FalsePositives.js
@@ -34,4 +34,15 @@ FalsePositives.schema = new SimpleSchema({
FalsePositives.attachSchema(FalsePositives.schema);
+export const falsePositivesRemap = (odoc) => {
+ const doc = odoc;
+ const geo = doc.geo;
+ doc.lat = geo.coordinates[1];
+ doc.lon = geo.coordinates[0];
+ doc._id = doc.fireId;
+ doc.id = doc.fireId;
+ delete doc.geo;
+ return doc;
+};
+
export default FalsePositives;
diff --git a/imports/api/FalsePositives/server/publications.js b/imports/api/FalsePositives/server/publications.js
index 9585907..18305c4 100644
--- a/imports/api/FalsePositives/server/publications.js
+++ b/imports/api/FalsePositives/server/publications.js
@@ -5,6 +5,8 @@
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import { NumberBetween } from '/imports/modules/server/other-checks';
+import L from 'leaflet-headless';
+import calcUnion from '/imports/ui/components/Maps/SubsUnion/Unify';
import FalsePositives from '../FalsePositives';
const counter = new Counter('countFalsePositives', FalsePositives.find({}));
@@ -13,6 +15,33 @@ Meteor.publish('falsePositivesTotal', function total() {
return counter;
});
+export const whichAreFalsePositives = (fires) => {
+ const group = new L.FeatureGroup();
+ const remap = fires.fetch().map(function remap(doc) {
+ return { location: { lat: doc.lat, lon: doc.lon }, distance: doc.scan };
+ });
+ const result = calcUnion(remap, group, sub => sub);
+
+ const falsePos = FalsePositives.find({
+ geo: {
+ $geoWithin: {
+ $geometry: result[0].geometry
+ }
+ }
+ }, {
+ fields: {
+ geo: 1,
+ // type: 1,
+ // when: 1,
+ fireId: 1
+ }
+ });
+
+ /* console.log(`False positive total: ${falsePos.count()}`);
+ console.log(`False positives: ${JSON.stringify(falsePos.fetch())}`); */
+ return falsePos;
+};
+
const falsePositives = (northEastLng, northEastLat, southWestLng, southWestLat) => {
const fires = FalsePositives.find({
geo: {
diff --git a/imports/api/Fires/server/publications.js b/imports/api/Fires/server/publications.js
index 0f0b80c..1dba8dc 100644
--- a/imports/api/Fires/server/publications.js
+++ b/imports/api/Fires/server/publications.js
@@ -9,6 +9,7 @@ import NodeGeocoder from 'node-geocoder';
import { gmapServerKey } from '/imports/startup/server/IPGeocoder';
import ActiveFiresCollection from '/imports/api/ActiveFires/ActiveFires';
import FireAlertsCollection from '/imports/api/FireAlerts/FireAlerts';
+import { whichAreFalsePositives } from '/imports/api/FalsePositives/server/publications';
import FiresCollection from '../Fires';
function findFire(unsealed) {
@@ -114,13 +115,14 @@ Meteor.publish('fireFromId', function fireFromId(_id) {
const fire = FiresCollection.find(new Meteor.Collection.ObjectID(_id));
if (fire.count() !== 0) {
// console.info(`Archive fire found: ${_id}`);
- return fire;
+ const falsePos = whichAreFalsePositives(fire);
+ return [fire, falsePos];
}
console.info(`Fire not found: ${_id}`);
// Not found in active fires!
return this.ready();
} catch (e) {
- console.info(`Active fire not found (with error): ${_id}`);
+ console.info(`Archive fire not found (with error): ${_id}`);
return this.ready();
}
});
diff --git a/imports/ui/pages/Fires/Fires.js b/imports/ui/pages/Fires/Fires.js
index 662b234..528e476 100644
--- a/imports/ui/pages/Fires/Fires.js
+++ b/imports/ui/pages/Fires/Fires.js
@@ -6,7 +6,7 @@ import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { withTracker } from 'meteor/react-meteor-data';
import { translate, Trans } from 'react-i18next';
-import { FormGroup } from 'react-bootstrap';
+import { Row, Col, Alert, FormGroup } from 'react-bootstrap';
import { Meteor } from 'meteor/meteor';
import { Bert } from 'meteor/themeteorchef:bert';
import { Helmet } from 'react-helmet';
@@ -15,10 +15,12 @@ import Blaze from 'meteor/gadicc:blaze-react-component';
import DefMapLayers from '/imports/ui/components/Maps/DefMapLayers';
import NotFound from '/imports/ui/pages/NotFound/NotFound';
import FiresCollection from '/imports/api/Fires/Fires';
+import FireList from '/imports/ui/components/Maps/FireList';
import FromNow from '/imports/ui/components/FromNow/FromNow';
import { dateLongFormat } from '/imports/api/Common/dates';
import '/imports/startup/client/comments';
import FalsePositiveTypes from '/imports/api/FalsePositives/FalsePositiveTypes';
+import FalsePositivesCollection, { falsePositivesRemap } from '/imports/api/FalsePositives/FalsePositives';
import ShareIt from '/imports/ui/components/ShareIt/ShareIt';
import './Fires.scss';
@@ -67,6 +69,7 @@ class Fire extends React.Component {
const {
notfound, loading, fire, t
} = this.props;
+ if (Meteor.isDevelopment) console.log(`False positives total: ${this.props.falsePositives.length}`);
/* console.log(`loading fire: ${loading}`);
* console.log(`Not found fire: ${notfound}`); */
if (fire && fire.when) {
@@ -105,6 +108,15 @@ class Fire extends React.Component {
radius={fire.scan ? fire.scan * 1000 : 300}
/>
+
{t('Coordenadas:')} {fire.lat}, {fire.lon}
@@ -119,6 +131,11 @@ class Fire extends React.Component {
{(fire.type !== 'vecinal') &&
+
+
+ Parece que este fuego no es un fuego forestal.
+
+
{t('¿No es un fuego forestal?')}
Indícanos de que tipo de fuego se trata y ayúdanos así a mejorar nuestras notificaciones:
@@ -172,6 +189,7 @@ Fire.propTypes = {
history: PropTypes.object.isRequired,
loading: PropTypes.bool.isRequired,
notfound: PropTypes.bool.isRequired,
+ falsePositives: PropTypes.arrayOf(PropTypes.object).isRequired,
fromHash: PropTypes.bool.isRequired,
active: PropTypes.bool.isRequired,
alert: PropTypes.bool.isRequired,
@@ -211,11 +229,13 @@ const FireContainer = withTracker(({ match }) => {
const notfound = !loading && FiresCollection.find().count() === 0;
/* console.log(`loading fire: ${loading}`);
* console.log(`Not found fire: ${notfound}`); */
+ const falsePositives = FalsePositivesCollection.find().fetch().map(falsePositivesRemap);
return {
loading,
active,
alert,
fromHash,
+ falsePositives,
fire: FiresCollection.findOne(),
notfound,
when: subscription.ready() && FiresCollection.findOne() ? FiresCollection.findOne().when : null
diff --git a/imports/ui/pages/FiresMap/FiresMap.js b/imports/ui/pages/FiresMap/FiresMap.js
index afd5dcd..da3605a 100644
--- a/imports/ui/pages/FiresMap/FiresMap.js
+++ b/imports/ui/pages/FiresMap/FiresMap.js
@@ -27,7 +27,7 @@ import DefMapLayers from '/imports/ui/components/Maps/DefMapLayers';
import FromNow from '/imports/ui/components/FromNow/FromNow';
import ActiveFiresCollection from '/imports/api/ActiveFires/ActiveFires';
import FireAlertsCollection from '/imports/api/FireAlerts/FireAlerts';
-import FalsePositivesCollection from '/imports/api/FalsePositives/FalsePositives';
+import FalsePositivesCollection, { falsePositivesRemap } from '/imports/api/FalsePositives/FalsePositives';
import SiteSettings from '/imports/api/SiteSettings/SiteSettings';
import { isNotHomeAndMobile, isChrome } from '/imports/ui/components/Utils/isMobile';
import { isHome } from '/imports/ui/components/Utils/location';
@@ -185,9 +185,8 @@ class FiresMap extends React.Component {
console.log(`Rendering ${this.props.loading ? 'loading' : 'LOADED'}, zoom ${this.state.viewport.zoom}, map ${this.props.activefires.length + this.props.firealerts.length} of ${this.props.activefirestotal} total. False positives: ${this.props.falsePositives.length}. Reactive ${this.state.viewport.zoom >= MAXZOOMREACTIVE}`);
const title = `${t('AppName')}: ${t('Fuegos activos')}`;
- if (Meteor.isDevelopment) {
- console.log(`False positives total: ${this.props.falsePositives.length}`);
- }
+ if (Meteor.isDevelopment) console.log(`False positives total: ${this.props.falsePositives.length}`);
+
return (
/* Large number of markers:
https://stackoverflow.com/questions/43015854/large-dataset-of-markers-or-dots-in-leaflet/43019740#43019740 */
@@ -375,16 +374,7 @@ export default translate([], { wait: true })(withTracker(() => {
const userSubs = SiteSettings.findOne({ name: 'subs-public-union' });
const userSubsBounds = SiteSettings.findOne({ name: 'subs-public-union-bounds' });
const fireAlerts = FireAlertsCollection.find().fetch();
- const falsePositives = FalsePositivesCollection.find().fetch().map((odoc) => {
- const doc = odoc;
- const geo = doc.geo;
- doc.lat = geo.coordinates[1];
- doc.lon = geo.coordinates[0];
- doc._id = doc.fireId;
- doc.id = doc.fireId;
- delete doc.geo;
- return doc;
- });
+ const falsePositives = FalsePositivesCollection.find().fetch().map(falsePositivesRemap);
return {
loading: !subscription ? true : !(subscription.ready() && settingsSubs.ready() && alertSubscription.ready()),
userSubs: userSubs ? userSubs.value : null,
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index def8ec6..1503c3f 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -206,5 +206,7 @@
"Reintentar ahora":
"Retry now",
"Feedback recibido, gracias...":
- "Feedback received, thanks..."
+ "Feedback received, thanks...",
+ "Parece que este fuego no es un fuego forestal.":
+ "It seems that this is not a forest fire."
}
diff --git a/public/locales/es/common.json b/public/locales/es/common.json
index c5e1efa..c36bc8b 100644
--- a/public/locales/es/common.json
+++ b/public/locales/es/common.json
@@ -293,5 +293,7 @@
"Reintentar ahora":
"Reintentar ahora",
"Feedback recibido, gracias...":
- "Feedback recibido, gracias..."
+ "Feedback recibido, gracias...",
+ "Parece que este fuego no es un fuego forestal.":
+ "Parece que este fuego no es un fuego forestal."
}
From 4d52b6c8e09a6cd38d61055bd122185dfb8dfe4f Mon Sep 17 00:00:00 2001
From: vjrj
Date: Tue, 20 Feb 2018 09:10:44 +0100
Subject: [PATCH 071/330] Fire page now shows also false positives
---
imports/ui/pages/Fires/Fires.js | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/imports/ui/pages/Fires/Fires.js b/imports/ui/pages/Fires/Fires.js
index 528e476..9ce3e44 100644
--- a/imports/ui/pages/Fires/Fires.js
+++ b/imports/ui/pages/Fires/Fires.js
@@ -131,11 +131,12 @@ class Fire extends React.Component {
{(fire.type !== 'vecinal') &&
-
-
- Parece que este fuego no es un fuego forestal.
-
-
+ { this.props.falsePositives.length > 0 &&
+
+
+ Parece que este fuego no es un fuego forestal.
+
+
}
{t('¿No es un fuego forestal?')}
Indícanos de que tipo de fuego se trata y ayúdanos así a mejorar nuestras notificaciones:
From e980bbdb2477d53f6db675768790e3925ffb7178 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Tue, 20 Feb 2018 09:11:07 +0100
Subject: [PATCH 072/330] Less clicks to show fire info
---
imports/ui/components/Maps/FireIconMark.js | 72 +++++++++++++---------
imports/ui/components/Maps/FirePopup.js | 14 +----
2 files changed, 45 insertions(+), 41 deletions(-)
diff --git a/imports/ui/components/Maps/FireIconMark.js b/imports/ui/components/Maps/FireIconMark.js
index 5e8af68..9817666 100644
--- a/imports/ui/components/Maps/FireIconMark.js
+++ b/imports/ui/components/Maps/FireIconMark.js
@@ -1,38 +1,54 @@
/* eslint-disable import/no-absolute-path */
-import React, { Fragment } from 'react';
+import React, { Component, Fragment } from 'react';
import { CircleMarker, Marker, Tooltip } from 'react-leaflet';
import PropTypes from 'prop-types';
import { fireIcon, nFireIcon, industryIcon } from '/imports/ui/components/Maps/Icons';
import { translate } from 'react-i18next';
import FirePopup from './FirePopup';
-const FireIconMark = ({
- lat,
- lon,
- nasa,
- id,
- history,
- falsePositives,
- when,
- t
-}) => (
-
- { !falsePositives &&
-
-
- }
- { falsePositives &&
-
- {t('Es una industria')}
- { /* disabled because was a past fire (and can be marked multiple times) */ false && }
-
- }
- { !falsePositives &&
-
-
- }
-
-);
+class FireIconMark extends Component {
+ constructor(props) {
+ super(props);
+ this.t = props.t;
+ this.onClick = this.onClick.bind(this);
+ }
+
+ onClick() {
+ // console.log('onClick fired');
+ this.props.history.push(`/fire/${this.props.nasa ? 'active' : 'alert'}/${this.props.id}`);
+ }
+
+ render() {
+ const {
+ lat,
+ lon,
+ nasa,
+ id,
+ history,
+ falsePositives,
+ when,
+ t
+ } = this.props;
+ return (
+
+ { !falsePositives &&
+
+
+ }
+ { falsePositives &&
+
+ {t('Es una industria')}
+ { /* disabled because was a past fire (and can be marked multiple times) */ false && }
+
+ }
+ { !falsePositives &&
+
+
+ }
+
+ );
+ }
+}
FireIconMark.propTypes = {
// https://github.com/PaulLeCam/react-leaflet/tree/master/src/propTypes
diff --git a/imports/ui/components/Maps/FirePopup.js b/imports/ui/components/Maps/FirePopup.js
index c917a6b..c2519b9 100644
--- a/imports/ui/components/Maps/FirePopup.js
+++ b/imports/ui/components/Maps/FirePopup.js
@@ -1,9 +1,8 @@
/* eslint-disable import/no-absolute-path */
import React, { Fragment } from 'react';
-import { Popup, Tooltip } from 'react-leaflet';
+import { Tooltip } from 'react-leaflet';
import PropTypes from 'prop-types';
import { translate } from 'react-i18next';
-import moment from 'moment';
const FirePopup = ({
lat,
@@ -15,17 +14,6 @@ const FirePopup = ({
t
}) => (
-
-
- {t('Coordenadas:')} {lat}, {lon}
- {nasa && {t('Fuente')}: {t(nasa ? 'NASA' : 'nuestros usuarios/as')} }
- {when && {t('Detectado')}: {moment(when).fromNow()} }
-
- { /* if nasa === null means that the is a false positive fire */ }
- history.push(`/fire/${nasa ? 'active' : 'alert'}/${id}`)}>{t('Más información sobre este fuego')}
-
-
-
{t('Pulsa para más información')}
);
From 84ab9f9cdc5e453584a0120fec1d208c6912eacc Mon Sep 17 00:00:00 2001
From: vjrj
Date: Tue, 20 Feb 2018 09:29:24 +0100
Subject: [PATCH 073/330] Move simple icon where used
---
imports/ui/components/CenterInMyPosition/CenterInMyPosition.js | 2 ++
imports/ui/layouts/App/App.js | 1 -
imports/ui/pages/Index/Index.js | 1 +
3 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/imports/ui/components/CenterInMyPosition/CenterInMyPosition.js b/imports/ui/components/CenterInMyPosition/CenterInMyPosition.js
index bff6360..44d7164 100644
--- a/imports/ui/components/CenterInMyPosition/CenterInMyPosition.js
+++ b/imports/ui/components/CenterInMyPosition/CenterInMyPosition.js
@@ -5,7 +5,9 @@ import { Tracker } from 'meteor/tracker';
import { ReactiveVar } from 'meteor/reactive-var';
import { Button } from 'react-bootstrap';
import { translate } from 'react-i18next';
+
import './CenterInMyPosition.scss';
+import 'simple-line-icons/css/simple-line-icons.css';
class CenterInMyPosition extends React.Component {
onClick() {
diff --git a/imports/ui/layouts/App/App.js b/imports/ui/layouts/App/App.js
index 4089c65..8190c6d 100644
--- a/imports/ui/layouts/App/App.js
+++ b/imports/ui/layouts/App/App.js
@@ -18,7 +18,6 @@ import '/imports/startup/client/meta';
import '/imports/startup/client/ravenLogger';
import '/imports/startup/client/geolocation';
import '/imports/startup/client/piwik-start.js';
-import 'simple-line-icons/css/simple-line-icons.css';
import { isHome } from '/imports/ui/components/Utils/location';
import Reconnect from '../../components/Reconnect/Reconnect';
import Navigation from '../../components/Navigation/Navigation';
diff --git a/imports/ui/pages/Index/Index.js b/imports/ui/pages/Index/Index.js
index 27c107a..0260aa1 100644
--- a/imports/ui/pages/Index/Index.js
+++ b/imports/ui/pages/Index/Index.js
@@ -19,6 +19,7 @@ import SubscriptionsMap from '/imports/ui/pages/Subscriptions/SubscriptionsMap';
import ShareIt from '/imports/ui/components/ShareIt/ShareIt';
import FiresMap from '../FiresMap/FiresMap';
+import 'simple-line-icons/css/simple-line-icons.css';
import './Index.scss';
import './Index-custom.scss';
From 0a7f6b8e40f8ab5d3ac941badd7820804dc9224a Mon Sep 17 00:00:00 2001
From: vjrj
Date: Tue, 20 Feb 2018 09:44:03 +0100
Subject: [PATCH 074/330] Use fa-icons
---
.../ui/components/CenterInMyPosition/CenterInMyPosition.js | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/imports/ui/components/CenterInMyPosition/CenterInMyPosition.js b/imports/ui/components/CenterInMyPosition/CenterInMyPosition.js
index 44d7164..c143a23 100644
--- a/imports/ui/components/CenterInMyPosition/CenterInMyPosition.js
+++ b/imports/ui/components/CenterInMyPosition/CenterInMyPosition.js
@@ -7,7 +7,6 @@ import { Button } from 'react-bootstrap';
import { translate } from 'react-i18next';
import './CenterInMyPosition.scss';
-import 'simple-line-icons/css/simple-line-icons.css';
class CenterInMyPosition extends React.Component {
onClick() {
@@ -40,8 +39,8 @@ class CenterInMyPosition extends React.Component {
const { onlyIcon, t } = this.props;
const msg = t('Centrar en tu ubicación');
return (
- this.onClick()}>
- {!onlyIcon ? msg : ''}
+ this.onClick()}>
+ {!onlyIcon ? msg : ''}
);
}
}
From 7df050a2b1509ea7cc3e2975805c55a30e16b44c Mon Sep 17 00:00:00 2001
From: vjrj
Date: Tue, 20 Feb 2018 09:44:29 +0100
Subject: [PATCH 075/330] Use gmaps in fire page for better false positive
detection
---
imports/ui/components/Maps/DefMapLayers.js | 6 +++---
imports/ui/pages/Fires/Fires.js | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/imports/ui/components/Maps/DefMapLayers.js b/imports/ui/components/Maps/DefMapLayers.js
index 79f3444..2fbdcb8 100644
--- a/imports/ui/components/Maps/DefMapLayers.js
+++ b/imports/ui/components/Maps/DefMapLayers.js
@@ -56,9 +56,9 @@ class DefMapLayers extends Component {
}
{ this.state.gkey &&
-
-
- }
+
+
+ }
);
}
diff --git a/imports/ui/pages/Fires/Fires.js b/imports/ui/pages/Fires/Fires.js
index 9ce3e44..8300054 100644
--- a/imports/ui/pages/Fires/Fires.js
+++ b/imports/ui/pages/Fires/Fires.js
@@ -117,7 +117,7 @@ class Fire extends React.Component {
nasa={false}
falsePositives
/>
-
+
{t('Coordenadas:')} {fire.lat}, {fire.lon}
{(fire.type === 'modis' || fire.type === 'viirs') &&
From 7ce93178767b8ff480e51f9df5b6baa4f6453a90 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Tue, 20 Feb 2018 13:39:05 +0100
Subject: [PATCH 076/330] Maps colors improved
---
imports/ui/components/Maps/DefMapLayers.js | 13 +++++++++----
imports/ui/components/SelectionMap/SelectionMap.js | 4 ++--
imports/ui/pages/Fires/Fires.js | 2 +-
imports/ui/pages/FiresMap/FiresMap.js | 2 +-
imports/ui/pages/Subscriptions/SubscriptionsMap.js | 2 +-
5 files changed, 14 insertions(+), 9 deletions(-)
diff --git a/imports/ui/components/Maps/DefMapLayers.js b/imports/ui/components/Maps/DefMapLayers.js
index 2fbdcb8..33da655 100644
--- a/imports/ui/components/Maps/DefMapLayers.js
+++ b/imports/ui/components/Maps/DefMapLayers.js
@@ -36,7 +36,7 @@ class DefMapLayers extends Component {
/>
);
const osmlayer = (
-
+
}
{ this.state.gkey &&
-
+
}
@@ -66,11 +66,16 @@ class DefMapLayers extends Component {
DefMapLayers.propTypes = {
t: PropTypes.func.isRequired,
- gray: PropTypes.bool
+ gray: PropTypes.bool,
+ osmcolor: PropTypes.bool,
+ satellite: PropTypes.bool
+
};
DefMapLayers.defaultProps = {
- gray: true
+ gray: false,
+ osmcolor: false,
+ satellite: false
};
export default translate([], { wait: true })(DefMapLayers);
diff --git a/imports/ui/components/SelectionMap/SelectionMap.js b/imports/ui/components/SelectionMap/SelectionMap.js
index cd8cfbd..94c065b 100644
--- a/imports/ui/components/SelectionMap/SelectionMap.js
+++ b/imports/ui/components/SelectionMap/SelectionMap.js
@@ -152,7 +152,7 @@ class SelectionMap extends Component {
subs: this.props.currentSubs
};
if (this.props.action === action.add) {
- subsOpts.color = '#F2F2F2';
+ subsOpts.color = 'gray';
}
this.state.union = subsUnion(this.state.union, subsOpts);
}
@@ -183,7 +183,7 @@ class SelectionMap extends Component {
wakeMessageTouch={t('Pulsa para activar')}
sleepOpacity={0.6}
>
-
+
{this.props.action === action.edit &&
this.props.currentSubs.map((subs, index) => (
-
+
{t('Coordenadas:')} {fire.lat}, {fire.lon}
{(fire.type === 'modis' || fire.type === 'viirs') &&
diff --git a/imports/ui/pages/FiresMap/FiresMap.js b/imports/ui/pages/FiresMap/FiresMap.js
index da3605a..1d26eb7 100644
--- a/imports/ui/pages/FiresMap/FiresMap.js
+++ b/imports/ui/pages/FiresMap/FiresMap.js
@@ -288,7 +288,7 @@ class FiresMap extends React.Component {
falsePositives={false}
/>
}
-
+
this.centerOnUserLocation(viewport)} onlyIcon {... this.props} />
diff --git a/imports/ui/pages/Subscriptions/SubscriptionsMap.js b/imports/ui/pages/Subscriptions/SubscriptionsMap.js
index 60bb37f..44e2b5d 100644
--- a/imports/ui/pages/Subscriptions/SubscriptionsMap.js
+++ b/imports/ui/pages/Subscriptions/SubscriptionsMap.js
@@ -129,7 +129,7 @@ class SubscriptionsMap extends React.Component {
wakeMessageTouch={this.props.t('Pulsa para activar')}
sleepOpacity={0.6}
>
-
+
this.centerOnUserLocation(viewport)} onlyIcon {... this.props} />
From 8b1d7f760232105e26743898e6a833a2538faeef Mon Sep 17 00:00:00 2001
From: vjrj
Date: Tue, 20 Feb 2018 20:49:23 +0100
Subject: [PATCH 077/330] Better zoom of fire pages
---
imports/ui/pages/Fires/Fires.js | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/imports/ui/pages/Fires/Fires.js b/imports/ui/pages/Fires/Fires.js
index 69d9637..24e54bb 100644
--- a/imports/ui/pages/Fires/Fires.js
+++ b/imports/ui/pages/Fires/Fires.js
@@ -49,6 +49,7 @@ class Fire extends React.Component {
}
}
+
shouldComponentUpdate(nextProps, nextState) {
return !(nextState.when === this.state.when && nextState.loading === this.state.loading && this.state.notfound === nextState.notfound);
}
@@ -65,6 +66,12 @@ class Fire extends React.Component {
});
}
+ handleLeafletLoad(circle) {
+ if (this.fireMap && this.fireMap.leafletElement && circle && circle.leafletElement) {
+ this.fireMap.leafletElement.fitBounds(circle.leafletElement.getBounds());
+ }
+ }
+
render() {
const {
notfound, loading, fire, t
@@ -89,14 +96,12 @@ class Fire extends React.Component {
{this.title}
{
- this.fireMap = map;
- }}
+ ref={(map) => { this.fireMap = map; }}
animate
sleep={false}
center={[fire.lat, fire.lon]}
className="fire-leaflet-container"
- zoom={12}
+ zoom={13}
>
{ this.circle = circle; this.handleLeafletLoad(circle); }}
/>
Date: Tue, 20 Feb 2018 20:50:59 +0100
Subject: [PATCH 078/330] Better zoom of fire pages
---
imports/ui/pages/Fires/Fires.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/imports/ui/pages/Fires/Fires.js b/imports/ui/pages/Fires/Fires.js
index 24e54bb..5662a3c 100644
--- a/imports/ui/pages/Fires/Fires.js
+++ b/imports/ui/pages/Fires/Fires.js
@@ -49,7 +49,6 @@ class Fire extends React.Component {
}
}
-
shouldComponentUpdate(nextProps, nextState) {
return !(nextState.when === this.state.when && nextState.loading === this.state.loading && this.state.notfound === nextState.notfound);
}
From f46aabb4d3c1dd900a414298eebbcc0fce08a867 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Wed, 21 Feb 2018 15:29:25 +0100
Subject: [PATCH 079/330] Added feedback
---
cucumber/features/feedback.feature | 22 ++++
cucumber/features/general.feature | 1 -
cucumber/features/pages.feature | 1 -
.../features/steps_definitions/feedback.js | 49 ++++++++
cucumber/features/steps_definitions/helper.js | 2 +
imports/ui/components/Feedback/Feedback.js | 106 ++++++++++--------
imports/ui/components/Utils/location.js | 20 ++--
imports/ui/layouts/App/App.js | 8 +-
8 files changed, 148 insertions(+), 61 deletions(-)
create mode 100644 cucumber/features/feedback.feature
create mode 100644 cucumber/features/steps_definitions/feedback.js
diff --git a/cucumber/features/feedback.feature b/cucumber/features/feedback.feature
new file mode 100644
index 0000000..d1ffed5
--- /dev/null
+++ b/cucumber/features/feedback.feature
@@ -0,0 +1,22 @@
+@watch
+Feature: Allow users to give feedback
+
+ As a user (authenticated or not)
+ I want to give feedback about this site
+
+ Scenario: A user can login and give feedback
+ Given I am on the zones page
+ And I have an account and I logged in
+ # Because is not verified
+ Then I can send feedback with email
+ When I sign out
+ Then I can send feedback with email
+
+ Scenario: A anon user can give feedback
+ Given I am on the fires page
+ Given I'm not logged
+ Then I can send feedback with email
+
+ Scenario: A user cannot give feedback in home
+ Given I am on the home page
+ Then I cannot send feedback
diff --git a/cucumber/features/general.feature b/cucumber/features/general.feature
index 3cf078d..b1fd8f9 100644
--- a/cucumber/features/general.feature
+++ b/cucumber/features/general.feature
@@ -1,6 +1,5 @@
Feature: This app should generate a correct sitemap, etc
- @watch
Scenario: I verify that a sitemap is generated correctly
Given the page sitemap.xml
Then I check that exist this list of pages in the sitemap
diff --git a/cucumber/features/pages.feature b/cucumber/features/pages.feature
index ed3f4fe..552ab8b 100644
--- a/cucumber/features/pages.feature
+++ b/cucumber/features/pages.feature
@@ -9,7 +9,6 @@ Feature: Test all secundary pages
| activeFires | Active Fires |
Then I check that all pages works properly
- @watch
Scenario: Check that all secondary pages work well
Given a list of page urls and contents
| login | Login | true |
diff --git a/cucumber/features/steps_definitions/feedback.js b/cucumber/features/steps_definitions/feedback.js
new file mode 100644
index 0000000..f25a3b3
--- /dev/null
+++ b/cucumber/features/steps_definitions/feedback.js
@@ -0,0 +1,49 @@
+/* global module expect require process client */
+
+const { randomText, randomEmail } = require('./helper.js');
+
+module.exports = function doSteps() {
+ this.Given(/^I am on the zones page$/, () => {
+ client.url(`${process.env.ROOT_URL}/zones`);
+ if (client.isVisible('#logout')) {
+ client.click('#logout');
+ }
+ });
+
+ this.Then(/^I can send feedback without email$/, () => {
+ client.waitForVisible('#feedback-tab', 10000);
+ client.click('#feedback-tab');
+ client.waitForVisible('#feedback-form', 10000);
+ client.setValue('#feedbackTextarea', randomText(500));
+ client.click('#sendFeedbackBtn');
+ client.waitForVisible('.bert-alert', 10000, true);
+ client.waitUntil(() => client.isVisible('.bert-alert') === false, 10000);
+ });
+
+ this.Then(/^I can send feedback with email$/, () => {
+ client.waitForVisible('#feedback-tab', 10000);
+ client.click('#feedback-tab');
+ client.waitForVisible('#feedback-form', 10000);
+ client.setValue('input[name="email"]', randomEmail());
+ client.setValue('#feedbackTextarea', randomText(500));
+ client.click('#sendFeedbackBtn');
+ client.waitForVisible('.bert-alert', 10000, true);
+ client.waitUntil(() => client.isVisible('.bert-alert') === false, 10000);
+ });
+
+ this.Given(/^I'm not logged$/, () => {
+ client.waitForVisible('#react-root', 5000);
+ if (client.isVisible('#logout')) {
+ client.click('#logout');
+ }
+ });
+
+ this.Given(/^I am on the fires page$/, () => {
+ client.url(`${process.env.ROOT_URL}/fires`);
+ });
+
+ this.Then(/^I cannot send feedback$/, () => {
+ client.waitForVisible('#react-root', 5000);
+ client.waitUntil(() => client.isVisible('#feedback-tab') === false, 10000);
+ });
+};
diff --git a/cucumber/features/steps_definitions/helper.js b/cucumber/features/steps_definitions/helper.js
index 9961796..7274a40 100644
--- a/cucumber/features/steps_definitions/helper.js
+++ b/cucumber/features/steps_definitions/helper.js
@@ -8,6 +8,8 @@ function makeid(size) {
return text;
}
+export const randomText = n => makeid(n);
+
export const randomUsername = () => makeid(7);
export const randomPassword = () => randomUsername();
diff --git a/imports/ui/components/Feedback/Feedback.js b/imports/ui/components/Feedback/Feedback.js
index c6b8919..26338cb 100644
--- a/imports/ui/components/Feedback/Feedback.js
+++ b/imports/ui/components/Feedback/Feedback.js
@@ -2,14 +2,16 @@
/* eslint-disable import/no-absolute-path */
/* eslint-disable import/no-absolute-path */
-import React, { Component } from 'react';
+import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import { Meteor } from 'meteor/meteor';
import { translate } from 'react-i18next';
import { FormGroup, Button, FormControl } from 'react-bootstrap';
import { Bert } from 'meteor/themeteorchef:bert';
import { withTracker } from 'meteor/react-meteor-data';
-import validate from '../../../modules/validate';
+import { isHome } from '/imports/ui/components/Utils/location';
+import { testId } from '/imports/ui/components/Utils/TestUtils';
+import validate from '/imports/modules/validate';
import './Feedback.scss';
@@ -67,50 +69,59 @@ class Feedback extends Component {
}
render() {
+ // console.log(`Render Feedback because isHome ${this.props.isHome}, email: '${this.props.emailAddress}'`);
+ const disabled = this.props.emailVerified && this.props.emailAddress;
return (
-
+ }
+
);
}
}
@@ -118,9 +129,12 @@ class Feedback extends Component {
Feedback.propTypes = {
t: PropTypes.func.isRequired,
emailAddress: PropTypes.string,
- emailVerified: PropTypes.bool.isRequired
+ emailVerified: PropTypes.bool.isRequired,
+ isHome: PropTypes.bool.isRequired
};
+
export default translate([], { wait: true })(withTracker(props => ({
emailAddress: props.emailAddress,
- emailVerified: props.emailVerified
+ emailVerified: props.emailVerified,
+ isHome: isHome()
}))(Feedback));
diff --git a/imports/ui/components/Utils/location.js b/imports/ui/components/Utils/location.js
index 804724a..3d4669a 100644
--- a/imports/ui/components/Utils/location.js
+++ b/imports/ui/components/Utils/location.js
@@ -1,14 +1,20 @@
+/* eslint-disable import/no-absolute-path */
+
import { Meteor } from 'meteor/meteor';
import { ReactiveVar } from 'meteor/reactive-var';
+import history from '/imports/ui/components/History/History';
-export const location = new ReactiveVar();
+export const location = new ReactiveVar(history.location.pathname);
-export const currentLocation = () => {
- if (Meteor.isClient) {
- return window.location.pathname;
- }
- return location.get();
-};
+if (Meteor.isClient) {
+ history.listen((loc) => { // , action) => {
+ // console.log(`The current URL is ${location.pathname}${location.search}${location.hash}`)
+ // console.log(`The last navigation action was ${action}`)
+ location.set(loc.pathname);
+ });
+}
+
+export const currentLocation = () => location.get();
export const currentLocationHref = () => {
if (Meteor.isClient) {
diff --git a/imports/ui/layouts/App/App.js b/imports/ui/layouts/App/App.js
index 8190c6d..3aa979d 100644
--- a/imports/ui/layouts/App/App.js
+++ b/imports/ui/layouts/App/App.js
@@ -18,7 +18,6 @@ import '/imports/startup/client/meta';
import '/imports/startup/client/ravenLogger';
import '/imports/startup/client/geolocation';
import '/imports/startup/client/piwik-start.js';
-import { isHome } from '/imports/ui/components/Utils/location';
import Reconnect from '../../components/Reconnect/Reconnect';
import Navigation from '../../components/Navigation/Navigation';
import Authenticated from '../../components/Authenticated/Authenticated';
@@ -139,8 +138,8 @@ const App = props => (
- { Meteor.isDevelopment && !props.isHome && }
+
{props.i18nReady.get() &&
}
@@ -161,8 +160,7 @@ App.propTypes = {
i18nReady: PropTypes.object.isRequired,
userId: PropTypes.string,
emailAddress: PropTypes.string,
- emailVerified: PropTypes.bool.isRequired,
- isHome: PropTypes.bool.isRequired
+ emailVerified: PropTypes.bool.isRequired
};
const getUserName = name => ({
@@ -178,7 +176,6 @@ export default withTracker(() => {
const name = user && user.profile && user.profile.name && getUserName(user.profile.name);
const emailAddress = user && user.emails && user.emails[0].address;
// console.log(`i18n ready?: ${i18nReady.get()}`);
- console.log(`isHome?: ${isHome()}`);
return {
loading,
loggingIn,
@@ -188,7 +185,6 @@ export default withTracker(() => {
roles: !loading && Roles.getRolesForUser(userId),
userId,
emailAddress,
- isHome: isHome(),
emailVerified: user && user.emails ? user && user.emails && user.emails[0].verified : true
};
})(App);
From ed16fe69b56650baccdcfb130448051f8752b5ce Mon Sep 17 00:00:00 2001
From: vjrj
Date: Wed, 21 Feb 2018 20:25:40 +0100
Subject: [PATCH 080/330] Increasing timeout in test
---
cucumber/features/steps_definitions/pages.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cucumber/features/steps_definitions/pages.js b/cucumber/features/steps_definitions/pages.js
index e309e15..38e61b0 100644
--- a/cucumber/features/steps_definitions/pages.js
+++ b/cucumber/features/steps_definitions/pages.js
@@ -43,7 +43,7 @@ module.exports = function () {
for (let i = 0; i < pages.length; i += 1) {
client.url(`${process.env.ROOT_URL}/${pages[i][0]}`);
const content = pages[i][1];
- client.waitForVisible('#react-root', 5000);
+ client.waitForVisible('#react-root', 10000);
client.waitForText('#react-root', content);
}
callback();
From a296fc6051eb6fe7d03a8892539c6fb8be7b32ef Mon Sep 17 00:00:00 2001
From: vjrj
Date: Wed, 21 Feb 2018 20:50:44 +0100
Subject: [PATCH 081/330] Change feedback email
---
imports/startup/server/feedback.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/imports/startup/server/feedback.js b/imports/startup/server/feedback.js
index 98ebaca..c1d028b 100644
--- a/imports/startup/server/feedback.js
+++ b/imports/startup/server/feedback.js
@@ -12,7 +12,7 @@ Meteor.methods({
check(feedback, String);
const appName = i18n.t('AppName');
sendEmail({
- to: 'info@comunes.org',
+ to: 'fires@comunes.org',
from: `${appName} `,
subject: `Feedback de ${email}!`,
sendAt: new Date(),
From ae3b9e5faf76107570363a9b1bab221470cda1be Mon Sep 17 00:00:00 2001
From: vjrj
Date: Thu, 22 Feb 2018 10:49:15 +0100
Subject: [PATCH 082/330] About page
---
cucumber/features/pages.feature | 1 +
imports/startup/server/sitemaps.js | 2 +-
imports/ui/components/Footer/Footer.js | 8 +++++-
imports/ui/components/Footer/Footer.scss | 8 ++++--
.../ui/components/PageHeader/PageHeader.scss | 2 +-
imports/ui/layouts/App/App.js | 2 ++
imports/ui/pages/About/About.js | 19 +++++++++++++
imports/ui/stylesheets/custom.scss | 20 +++++++++++++-
private/pages/about-en.md | 26 ++++++++++++++++++
private/pages/about-es.md | 26 ++++++++++++++++++
public/locales/en/common.json | 10 ++++++-
public/locales/es/common.json | 10 ++++++-
public/photos/about-1-peq.jpg | Bin 0 -> 394780 bytes
public/photos/about-1.jpg | Bin 0 -> 512573 bytes
14 files changed, 126 insertions(+), 8 deletions(-)
create mode 100644 imports/ui/pages/About/About.js
create mode 100644 private/pages/about-en.md
create mode 100644 private/pages/about-es.md
create mode 100644 public/photos/about-1-peq.jpg
create mode 100644 public/photos/about-1.jpg
diff --git a/cucumber/features/pages.feature b/cucumber/features/pages.feature
index 552ab8b..1c17a30 100644
--- a/cucumber/features/pages.feature
+++ b/cucumber/features/pages.feature
@@ -7,6 +7,7 @@ Feature: Test all secundary pages
| credits | Credits |
| privacy | Privacy Policy |
| activeFires | Active Fires |
+ | about | About |
Then I check that all pages works properly
Scenario: Check that all secondary pages work well
diff --git a/imports/startup/server/sitemaps.js b/imports/startup/server/sitemaps.js
index 29a62df..67559a7 100644
--- a/imports/startup/server/sitemaps.js
+++ b/imports/startup/server/sitemaps.js
@@ -16,7 +16,7 @@ sitemaps.add('/sitemap.xml', () => {
out.push({ page: '/terms', lastmod: today });
out.push({ page: '/license', lastmod: today });
out.push({ page: '/privacy', lastmod: today });
-
+ out.push({ page: '/about', lastmod: today });
// When user has public page
/* const users = Meteor.users.find().fetch();
diff --git a/imports/ui/components/Footer/Footer.js b/imports/ui/components/Footer/Footer.js
index 23fd605..d84665f 100644
--- a/imports/ui/components/Footer/Footer.js
+++ b/imports/ui/components/Footer/Footer.js
@@ -23,10 +23,16 @@ const Footer = (props) => {
© Copyleft {copyrightYear()} {t('OrgName')} {t('OrgName')}
+
+
+ {t('Sobre nosotr@s')}
+ {t('Nosotr@s')}
+
+
{t('Términos de Servicio')}
- {t('Términos')}
+
diff --git a/imports/ui/components/Footer/Footer.scss b/imports/ui/components/Footer/Footer.scss
index d74554e..65779c8 100644
--- a/imports/ui/components/Footer/Footer.scss
+++ b/imports/ui/components/Footer/Footer.scss
@@ -63,7 +63,10 @@ body {
}
@include breakpoint(tablet) {
- .Footer ul li:first-child, .Footer ul li:nth-child(2) {
+ .Footer ul li:first-child,
+ .Footer ul li:nth-child(2),
+ .Footer ul li:nth-child(3),
+ .Footer ul li:nth-child(4){
margin-right: 15px;
}
}
@@ -85,7 +88,8 @@ body {
}
.Footer ul li:first-child,
- .Footer ul li:nth-child(2) {
+ .Footer ul li:nth-child(2),
+ .Footer ul li:nth-child(3) {
margin-right: 7px;
}
}
diff --git a/imports/ui/components/PageHeader/PageHeader.scss b/imports/ui/components/PageHeader/PageHeader.scss
index 3f7216f..2f8474f 100644
--- a/imports/ui/components/PageHeader/PageHeader.scss
+++ b/imports/ui/components/PageHeader/PageHeader.scss
@@ -3,7 +3,7 @@
.PageHeader {
border-bottom: 1px solid $gray-lighter;
- padding: 0px 0 20px;
+ padding: 10px 0 20px;
margin-bottom: 20px;
h1 {
diff --git a/imports/ui/layouts/App/App.js b/imports/ui/layouts/App/App.js
index 3aa979d..4011296 100644
--- a/imports/ui/layouts/App/App.js
+++ b/imports/ui/layouts/App/App.js
@@ -42,6 +42,7 @@ import FiresMap from '../../pages/FiresMap/FiresMap';
import Fires from '../../pages/Fires/Fires';
import Sandbox from '../../pages/Sandbox/Sandbox';
import Terms from '../../pages/Terms/Terms';
+import About from '../../pages/About/About';
import Privacy from '../../pages/Privacy/Privacy';
import License from '../../pages/License/License';
import Credits from '../../pages/Credits/Credits';
@@ -133,6 +134,7 @@ const App = props => (
+
diff --git a/imports/ui/pages/About/About.js b/imports/ui/pages/About/About.js
new file mode 100644
index 0000000..060cbe7
--- /dev/null
+++ b/imports/ui/pages/About/About.js
@@ -0,0 +1,19 @@
+/* eslint-disable import/no-absolute-path */
+/* eslint-disable react/jsx-indent-props */
+/* eslint-disable react/jsx-indent */
+
+import React from 'react';
+import { translate } from 'react-i18next';
+import Page from '../Page/Page';
+
+const About = props => (
+
+);
+
+export default translate([], { wait: true })(About);
diff --git a/imports/ui/stylesheets/custom.scss b/imports/ui/stylesheets/custom.scss
index 8bd13a4..e65d3f3 100644
--- a/imports/ui/stylesheets/custom.scss
+++ b/imports/ui/stylesheets/custom.scss
@@ -2,7 +2,7 @@
@import './colors';
a, a:hover {
- color: $todos-palette2;
+ color: $todos-palette1a;
}
.bg-dark {
@@ -50,3 +50,21 @@ div.leaflet-pane.leaflet-popup-pane > div > div.leaflet-popup-content-wrapper >
div.leaflet-pane.leaflet-popup-pane > div > div.leaflet-popup-content-wrapper > div > p > a {
// color: $todos-palette2;
}
+
+.bg-image-about-full {
+ background: no-repeat center center scroll;
+ -webkit-background-size: cover;
+ -moz-background-size: cover;
+ background-size: cover;
+ -o-background-size: cover;
+ background-image: url('/photos/about-1.jpg');
+ height: 600px;
+ margin: 0px -350px 20px -350px;
+}
+
+@include breakpoint(mobile) {
+ .bg-image-about-full {
+ background-image: url('/photos/about-1-peq.jpg');
+ /* height: 400px; other photo */
+ }
+}
diff --git a/private/pages/about-en.md b/private/pages/about-en.md
new file mode 100644
index 0000000..e683d48
--- /dev/null
+++ b/private/pages/about-en.md
@@ -0,0 +1,26 @@
+We are neighbors who at some point have been forced to put out intentional fires in the forest of our town, or we had to pack our bags because the fire surrounded our houses, or the fire has devastated our farms, or that simply this affects us.
+
+
+
+
+
+These intentional fires are common in Spain, where this initiative comes from. And this happens because not everyone is against forest fires: there are many [interests and motivations](http://www.naturalezacantabrica.es/2015/12/el-infierno-en-asturies-quien-quema-el.html), more than you can [imagine](http://ctxt.es/es/20170906/Politica/14827/ctxt-incendios-corrupci%C3%B3n-portugal-Avialsa-Faasa.htm).
+
+**In the north of Spain, usually humid, when the weather conditions are favorable, the arson fires multiply. This situation sometimes causes that emergency services become collapsed and can not go to all the [fires](https://elpais.com/elpais/2017/10/17/inenglish/1508231214_008450.html). So the neighbors have no choice but to [organize](https://www.vice.com/en_au/article/bjvwgv/photos-of-galicians-fighting-wildfires-together) themself.**
+
+The arsonists handle the same saturation so that their fires are more effective, and since the information that is sent from the individuals to the emergency services is not reverted to people willing to act or in danger, the defenselessness is total.
+
+**In the broken orography of our area, a fire can reach your back quite quickly. So the early and public detection of fires is crucial, especially when we have little means to combat them.**
+
+We imagine that there are digital tools of this type that our emergency services manage, but they are not public, at least not here. This encouraged us to develop these tools.
+
+In addition, according to friends from other latitudes, these problems happen in their countries also, so we have translated and shared with everyone as Free Software [these tools](https://github.com/comunes/todos-contra-el-fuego).
+
+We also maintain an archive as documentation of the fires. There, those affected by fire can provide information and thus help, for example, to investigate the causes of each fire. And we are developing more tools of this type.
+
+In such a difficult climate, in which the last thing we have to do is emit more CO2, we believe that we must take more legal measures before reaching this point where certain people profit from fires and other people like us, we have to shut them down.
+
+You can participate in this initiative, from helping to monitor your area, spreanding the word about this initiative among your contacts, helping to translate these tools into your language, contributing ideas or developing them, and/or helping to pay for the [expenses of our servers](https://comunes.org/donate). You can contact us if you have more questions at [fires@comunes.org](mailto:fires@comunes.org).
+
+Thank you,
diff --git a/private/pages/about-es.md b/private/pages/about-es.md
new file mode 100644
index 0000000..b10a471
--- /dev/null
+++ b/private/pages/about-es.md
@@ -0,0 +1,26 @@
+Somos vecinos y vecinas que en algún momento nos hemos visto obligados a apagar fuegos intencionados en el bosque de nuestro pueblo, o hemos tenido que hacer las maletas porque el fuego rodeaba nuestras casas, o el fuego ha arrasado nuestras fincas, o que simplemente este tema nos afecta.
+
+
+
+
+
+Estos fuegos intencionados son comunes en España, de donde surge esta iniciativa. Y es que no todo el mundo está contra los incendios forestales: hay muchos [intereses y motivos](http://www.naturalezacantabrica.es/2015/12/el-infierno-en-asturies-quien-quema-el.html), más de los que os podáis [imaginar](http://ctxt.es/es/20170906/Politica/14827/ctxt-incendios-corrupci%C3%B3n-portugal-Avialsa-Faasa.htm).
+
+**En el norte de España, por lo general húmedo, cuando las condiciones meteorológicas son propicias, los incendios provocados se multiplican. Esto genera en ocasiones que los servicios de emergencia se colapsen y [no puedan acudir a todos los fuegos](https://www.elconfidencial.com/espana/galicia/2017-10-16/incendios-galicia-asturias-redes-sociales-vecinos-ayuda_1461470/). Así que a los vecinos no nos queda otra que [organizarnos](https://elpais.com/elpais/2017/10/17/inenglish/1508231214_008450.html).**
+
+Los incendiarios son más conscientes que nadie de este colapso y lo utilizan para que sus fuegos sean más efectivos. Como los avisos de fuegos no se comunican a las personas dispuestas a actuar o en peligro, la indefensión es total.
+
+**En la orografía quebrada de nuestra zona, un fuego te gana la espalda y no te enteras. Así que la detección tempranay pública de fuegos es crucial, más cuando tenemos pocos medios para combatirlos.**
+
+Imaginamos que existen herramientas digitales de este tipo que gestionan los servicios de emergencia, pero no son públicos, por lo menos no aquí. Por eso nos animamos a desarrollar estas herramientas.
+
+Además, según nos cuentan amigos de otras latitudes estos mismos problemas suceden en sus países, así que las hemos traducido y compartido con todo el mundo como [Software Libre](https://github.com/comunes/todos-contra-el-fuego).
+
+También mantenemos un archivo documental de los fuegos en el que los afectados puedan aportar información y así ayudar, por ejemplo, a la investigación sobre las causas de cada fuego. Y estamos desarrollando más herramientas de este tipo.
+
+En un momento climático tan difícil, en el que lo último que tenemos que hacer es emitir más CO2, creemos que hay que tomar más medidas legales antes de llegar a este punto en el que ciertas personas se lucran de los incendios y otras personas como nosotras tengamos que andar apagándolos.
+
+Puedes participar en esta iniciativa, desde ayudando a vigilar tu zona, difundiendo esta iniciativa entre tus contactos, ayudando a traducir estas herramientas a tu idioma, aportando ideas o desarrollándolas, y/o ayudando a costear los gastos de [nuestros servidores](https://comunes.org/donate). Puedes contactarnos si tienes más dudas en [fuegos@comunes.org](mailto:fuegos@comunes.org).
+
+Gracias,
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index 1503c3f..ad31218 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -208,5 +208,13 @@
"Feedback recibido, gracias...":
"Feedback received, thanks...",
"Parece que este fuego no es un fuego forestal.":
- "It seems that this is not a forest fire."
+ "It seems that this is not a forest fire.",
+ "Sobre nosotr@s":
+ "About us",
+ "Nosotr@s":
+ "About us",
+ "Sobre '{{appName}}'":
+ "About '{{appName}}'",
+ "¿Vecindarios vigilando y combatiendo fuegos? ¿de qué va todo esto?":
+ "Neighborhoods monitoring and fighting fires? What is all this about?"
}
diff --git a/public/locales/es/common.json b/public/locales/es/common.json
index c36bc8b..cec8929 100644
--- a/public/locales/es/common.json
+++ b/public/locales/es/common.json
@@ -295,5 +295,13 @@
"Feedback recibido, gracias...":
"Feedback recibido, gracias...",
"Parece que este fuego no es un fuego forestal.":
- "Parece que este fuego no es un fuego forestal."
+ "Parece que este fuego no es un fuego forestal.",
+ "Sobre nosotr@s":
+ "Sobre nosotr@s",
+ "Nosotr@s":
+ "Nosotr@s",
+ "Sobre '{{appName}}'":
+ "Sobre '{{appName}}'",
+ "¿Vecindarios vigilando y combatiendo fuegos? ¿de qué va todo esto?":
+ "¿Vecindarios vigilando y combatiendo fuegos? ¿de qué va todo esto?"
}
diff --git a/public/photos/about-1-peq.jpg b/public/photos/about-1-peq.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..f99eeaf1e157d6141a740d6382ea67f52260dc15
GIT binary patch
literal 394780
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 }q^L$vZ*c`PTZ1sM7_&<_AW
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