From eca58df6c2de24d9bc6f0e49ddbbe1faadd7939e Mon Sep 17 00:00:00 2001 From: vjrj Date: Thu, 8 Feb 2018 11:15:23 +0100 Subject: [PATCH] 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 => (