From a0e5dc4723f0e119fff03931a6017da07a014870 Mon Sep 17 00:00:00 2001
From: vjrj
Date: Sat, 9 Dec 2017 17:54:18 +0100
Subject: [PATCH] i18n in server. Some fixes
---
.meteor/packages | 1 +
.meteor/versions | 1 +
.../api/Subscriptions/server/publications.js | 13 +-
imports/startup/client/i18n.js | 84 +----
imports/startup/common/i18n.js | 56 ++++
imports/startup/server/email.js | 78 ++++-
imports/startup/server/i18n.js | 31 ++
imports/startup/server/index.js | 1 +
.../components/SelectionMap/SelectionMap.js | 14 +-
imports/ui/pages/FiresMap/FiresMap.js | 78 ++---
imports/ui/pages/Sandbox/Sandbox.js | 3 +-
package-lock.json | 299 ++++++++++++++++++
package.json | 5 +
13 files changed, 543 insertions(+), 121 deletions(-)
create mode 100644 imports/startup/common/i18n.js
create mode 100644 imports/startup/server/i18n.js
diff --git a/.meteor/packages b/.meteor/packages
index 33ce009..2f876f3 100644
--- a/.meteor/packages
+++ b/.meteor/packages
@@ -45,3 +45,4 @@ natestrauser:publish-performant-counts
maximum:server-transform
mys:fonts # fonst in npm
ostrio:mailer # mailer
+ostrio:meteor-root
diff --git a/.meteor/versions b/.meteor/versions
index 7e88bbb..0508941 100644
--- a/.meteor/versions
+++ b/.meteor/versions
@@ -84,6 +84,7 @@ observe-sequence@1.0.16
ordered-dict@1.0.9
ostrio:cookies@2.1.3
ostrio:mailer@2.0.6
+ostrio:meteor-root@1.0.6
peerlibrary:assert@0.2.5
peerlibrary:fiber-utils@0.6.0
peerlibrary:reactive-mongo@0.1.1
diff --git a/imports/api/Subscriptions/server/publications.js b/imports/api/Subscriptions/server/publications.js
index 508900f..6257cb5 100644
--- a/imports/api/Subscriptions/server/publications.js
+++ b/imports/api/Subscriptions/server/publications.js
@@ -4,22 +4,21 @@ import Perlin from 'loms.perlin';
Perlin.seed(Math.random());
-Meteor.publishTransformed('userSubsToFires', function() {
-
+Meteor.publishTransformed('userSubsToFires', function () {
// 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(doc) {
- var location = doc.location;
+ return Subscriptions.find().serverTransform(function (doc) {
+ var location = doc.location;
/* doc.lat = location.lat;
- * doc.lon = location.lon;*/
+ * doc.lon = location.lon; */
if (location) {
doc.lat = Math.round(location.lat * 10) / 10;
doc.lon = Math.round(location.lon * 10) / 10;
}
// console.log(`[${doc.lat}, ${doc.lon}]`);
- var noiseBase = Perlin.perlin2(doc.lat, doc.lon)
- var noise = Math.abs(noiseBase/3);
+ var noiseBase = Perlin.perlin2(doc.lat, doc.lon);
+ var noise = Math.abs(noiseBase / 3);
// console.log(`Noise ${noise}, abs: ${Math.abs(noise)}`);
doc.lat += noise;
doc.lon += noise;
diff --git a/imports/startup/client/i18n.js b/imports/startup/client/i18n.js
index 713bccc..3bd05a6 100644
--- a/imports/startup/client/i18n.js
+++ b/imports/startup/client/i18n.js
@@ -1,12 +1,11 @@
+/* global CookieConsent Intl */
import i18n from 'i18next';
import backend from 'i18next-xhr-backend';
import LngDetector from 'i18next-browser-languagedetector';
import Cache from 'i18next-localstorage-cache';
-import { Meteor } from 'meteor/meteor';
import { T9n } from 'meteor-accounts-t9n';
-import en from 'meteor-accounts-t9n/build/en';
-import es from 'meteor-accounts-t9n/build/es';
import moment from 'moment';
+import i18nOpts from '../common/i18n';
// Adapted from: https://github.com/appigram/ryfma-boilerplate/blob/44c1eabfb9928b5623afab36a23997969e5beb02/imports/startup/client/i18n.js
@@ -21,7 +20,7 @@ const detectorOptions = {
// cache user language on
caches: ['localStorage', 'cookie'],
- excludeCacheFor: ['cimode'], // languages to not persist (cookie, localStorage)
+ excludeCacheFor: ['cimode'] // languages to not persist (cookie, localStorage)
};
const cacheOptions = {
@@ -32,78 +31,29 @@ const cacheOptions = {
// expiration
expirationTime: 7 * 24 * 60 * 60 * 1000,
// language versions
- versions: {},
+ versions: {}
};
-var backOpts = {
- // path where resources get loaded from
- loadPath: '/locales/{{lng}}/{{ns}}.json',
-
- // path to post missing resources
- addPath: '/locales/{{lng}}/{{ns}}.missing.json',
-
- // jsonIndent to use when storing json files
- jsonIndent: 2
+i18nOpts.cache = cacheOptions;
+i18nOpts.detection = detectorOptions;
+i18nOpts.react = {
+ wait: true
+ // https://react.i18next.com/components/i18next-instance.ht
+ /* bindI18n: 'languageChanged loaded',
+ bindStore: 'added removed',
+ nsMode: 'default' */
};
-T9N_LANGUAGES='es,en';
-
-const forceDebug = false;
-const shouldDebug = (forceDebug && !Meteor.isProduction);
-
i18n.use(backend)
.use(LngDetector)
.use(Cache)
- .init({
- backend: backOpts,
- lng: 'es',
- //fallbackLng: 'es',
- fallbackLng: {
- 'en-US': ['en'],
- 'en-GB': ['en'],
- 'pt-BR': ['pt'],
- 'default': ['es']
- },
- interpolation: {
- escapeValue: false, // not needed for react!!
- formatSeparator: ",",
- format: function(value, format, lng) {
- // https://www.i18next.com/formatting.html
- // console.log(`Value: ${value} with format: ${format} to lang: ${lng}`);
- if (format === 'uppercase') return value.toUpperCase();
- if (value instanceof Date) return moment(value).format(format);
- if (format === 'number') return Intl.NumberFormat(lng).format(value);
- return value;
- }
- },
- whitelist: false,
- // whitelist: ['es', 'en'], // allowed languages
- load: 'all', // es-ES -> es, en-US -> en
- debug: shouldDebug,
- ns: 'common',
- defaultNS: 'common',
- saveMissing: shouldDebug, // if true seems it's fails to getResourceBundle
- saveMissingTo: 'es',
- keySeparator: 'ß',
- nsSeparator: 'ð',
- pluralSeparator: 'đ',
- cache: cacheOptions,
- detection: detectorOptions,
- react: {
- wait: true,
- // https://react.i18next.com/components/i18next-instance.ht
- /* bindI18n: 'languageChanged loaded',
- bindStore: 'added removed',
- nsMode: 'default' */
- }
- }, function(err, t) {
+ .init(i18nOpts, (err, t) => {
// initialized and ready to
if (err) {
console.error(err);
return;
}
- document.title = t("AppName");
-
+ document.title = t('AppName');
// Accounts translation
// https://github.com/softwarerero/meteor-accounts-t9n
// console.log("Language: " + i18n.language);
@@ -111,11 +61,11 @@ i18n.use(backend)
// console.log(T9n.get('error.accounts.User not found'));
// cookies eu consent
- var cookiesOpt = {
+ const cookiesOpt = {
cookieTitle: t('Uso de Cookies'),
cookieMessage: t('Utilizamos cookies para asegurar un mejor uso de nuestra web. Si continúas navegando, consideramos que aceptas su uso'),
/* cookieMessage: t('Uso de Cookies'),
- cookieMessageImply: t('Utilizamos cookies para asegurar un mejor uso de nuestra web. Si continúas navegando, consideramos que aceptas su uso'),*/
+ cookieMessageImply: t('Utilizamos cookies para asegurar un mejor uso de nuestra web. Si continúas navegando, consideramos que aceptas su uso'), */
showLink: false,
position: 'bottom',
linkText: 'Lee más',
@@ -129,7 +79,7 @@ i18n.use(backend)
CookieConsent.init(cookiesOpt);
});
-i18n.on('languageChanged', function(lng) {
+i18n.on('languageChanged', (lng) => {
moment.locale(lng);
T9n.setLanguage(lng);
});
diff --git a/imports/startup/common/i18n.js b/imports/startup/common/i18n.js
new file mode 100644
index 0000000..d54e263
--- /dev/null
+++ b/imports/startup/common/i18n.js
@@ -0,0 +1,56 @@
+import { Meteor } from 'meteor/meteor';
+import moment from 'moment';
+// Load the js langs
+import es from 'meteor-accounts-t9n/build/es';
+import en from 'meteor-accounts-t9n/build/en';
+
+var backOpts = {
+ // path where resources get loaded from
+ loadPath: '/locales/{{lng}}/{{ns}}.json',
+
+ // path to post missing resources
+ addPath: '/locales/{{lng}}/{{ns}}.missing.json',
+
+ // jsonIndent to use when storing json files
+ jsonIndent: 2
+};
+
+const forceDebug = false;
+const shouldDebug = (forceDebug && !Meteor.isProduction);
+
+var i18nOpts = {
+ backend: backOpts,
+ lng: 'es',
+ // fallbackLng: 'es',
+ fallbackLng: {
+ 'en-US': ['en'],
+ 'en-GB': ['en'],
+ 'pt-BR': ['pt'],
+ 'default': ['es']
+ },
+ interpolation: {
+ escapeValue: false, // not needed for react!!
+ formatSeparator: ',',
+ format: function (value, format, lng) {
+ // https://www.i18next.com/formatting.html
+ // console.log(`Value: ${value} with format: ${format} to lang: ${lng}`);
+ if (format === 'uppercase') return value.toUpperCase();
+ if (value instanceof Date) return moment(value).format(format);
+ if (format === 'number') return Intl.NumberFormat(lng).format(value);
+ return value;
+ }
+ },
+ whitelist: false,
+ // whitelist: ['es', 'en'], // allowed languages
+ load: 'all', // es-ES -> es, en-US -> en
+ debug: shouldDebug,
+ ns: 'common',
+ defaultNS: 'common',
+ saveMissing: shouldDebug, // if true seems it's fails to getResourceBundle
+ saveMissingTo: 'es',
+ keySeparator: 'ß',
+ nsSeparator: 'ð',
+ pluralSeparator: 'đ'
+};
+
+export default i18nOpts;
diff --git a/imports/startup/server/email.js b/imports/startup/server/email.js
index 377599c..c0728e6 100644
--- a/imports/startup/server/email.js
+++ b/imports/startup/server/email.js
@@ -1,3 +1,79 @@
import { Meteor } from 'meteor/meteor';
+import nodemailer from 'nodemailer';
+import { MailTime } from 'meteor/ostrio:mailer';
+import i18n from 'i18next';
-if (Meteor.isDevelopment) process.env.MAIL_URL = Meteor.settings.private.MAIL_URL;
+// console.log(i18n.t('Inicio del mailer'));
+
+const transports = [];
+
+// First transport
+const fstTransport = nodemailer.createTransport(Meteor.settings.private.MAIL_URL);
+transports.push(fstTransport);
+// console.log(fstTransport.options.auth.user);
+
+const db = Meteor.users.rawDatabase(); // new Mongo.Collection('__mailTimeQueue__').rawDatabase();
+
+// Use __mailTimeQueue collection in any db
+// db.getCollection("__mailTimeQueue__").count()
+
+// https://litmus.com/community/discussions/4633-is-there-a-reliable-1px-horizontal-rule-method
+const hr = ``;
+
+const MailQueue = new MailTime({
+ db,
+ type: 'server',
+ strategy: 'balancer', // Transports will be used in round robin chain
+ transports,
+ from(transport) {
+ // To pass spam-filters `from` field should be correctly set
+ // for each transport, check `transport` object for more options
+ return `"${i18n.t('AppName')}" <${transport.options.auth.user}>`;
+ },
+ debug: true,
+ concatEmails: true, // Concatenate emails to the same addressee
+ concatSubject: `${i18n.t('Nuevas notificaciones de {{app}}', { app: i18n.t('AppName') })}`,
+ /* eslint-disable */
+ concatDelimiter: hr + '{{{subject}}}
', // Start each concatenated email with it's own subject
+ /* eslint-enable */
+ // concatThrottling: 30,
+ template: MailTime.Template // Use default template
+});
+
+// A Client (not used yet)
+// const MailQueueClient = new MailTime({
+// db,
+// type: 'client',
+// debug: true,
+// strategy: 'balancer', // Transports will be used in round robin chain
+// concatEmails: true // Concatenate emails to the same address
+// });
+
+export default function sendMail(opts, debug) {
+ if (debug) {
+ MailQueue.sendMail(opts, (err, info) => { if (err) { console.error(err); } else { console.log(info); } });
+ } else {
+ MailQueue.sendMail(opts);
+ }
+}
+
+if (Meteor.settings.private.testMailer) {
+ const emailOpts = {
+ to: Meteor.settings.private.testEmail,
+ userName: 'someone',
+ sendAt: new Date(),
+ subject: 'Some new notification',
+ text: 'Plain text message',
+ template: '{{appName}}{{{subject}}}
{{{html}}}',
+ appName: i18n.t('AppName'),
+ html: 'Styled message
'
+ };
+ sendMail(emailOpts, true);
+ sendMail(emailOpts, true);
+ sendMail(emailOpts, true);
+ sendMail(emailOpts, true);
+}
diff --git a/imports/startup/server/i18n.js b/imports/startup/server/i18n.js
new file mode 100644
index 0000000..f2652b4
--- /dev/null
+++ b/imports/startup/server/i18n.js
@@ -0,0 +1,31 @@
+import { Meteor } from 'meteor/meteor';
+import i18n from 'i18next';
+import backend from 'i18next-sync-fs-backend';
+import i18nOpts from '../common/i18n';
+// import moment from 'moment';
+// import { T9n } from 'meteor-accounts-t9n';
+
+i18nOpts.backend.loadPath = `${Meteor.absolutePath}/public${i18nOpts.backend.loadPath}`;
+i18nOpts.backend.addPath = `${Meteor.absolutePath}/public${i18nOpts.backend.addPath}`;
+
+// console.log(i18nOpts.backend.loadPath);
+i18nOpts.debug = false;
+i18nOpts.saveMissing = true;
+i18nOpts.initImmediate = false;
+
+i18n
+ .use(backend)
+ .init(i18nOpts, (err) => {
+ if (err) {
+ console.error(err);
+ }
+ });
+
+/* export function setUserLang (lng) {
+ * moment.locale(lng);
+ * T9n.setLanguage(lng);
+ * } */
+
+// console.log(i18n.t('Servidor arrancado'));
+
+export default i18n;
diff --git a/imports/startup/server/index.js b/imports/startup/server/index.js
index 6c4282c..9ba16f8 100644
--- a/imports/startup/server/index.js
+++ b/imports/startup/server/index.js
@@ -1,3 +1,4 @@
+import './i18n';
import './accounts';
import './api';
import './fixtures';
diff --git a/imports/ui/components/SelectionMap/SelectionMap.js b/imports/ui/components/SelectionMap/SelectionMap.js
index b8cbcd5..d424022 100644
--- a/imports/ui/components/SelectionMap/SelectionMap.js
+++ b/imports/ui/components/SelectionMap/SelectionMap.js
@@ -18,10 +18,10 @@ const positionIcon = new Leaflet.Icon({
/* shadowUrl: require('../public/marker-shadow.png'), */
iconSize: [50, 77], // size of the icon
/* shadowSize: [50, 64], // size of the shadow */
- iconAnchor: [25, 82], // point of the icon which will correspond to marker's location
+ iconAnchor: [25, 82] // point of the icon which will correspond to marker's location
/* shadowAnchor: [4, 62], // the same for the shadow
* popupAnchor: [-3, -76]// point from which the popup should open relative to the iconAnchor*/
-})
+});
class SelectionMap extends Component {
constructor(props) {
@@ -45,11 +45,11 @@ class SelectionMap extends Component {
}
toggleDraggable = () => {
- this.setState({ draggable: !this.state.draggable })
+ this.setState({ draggable: !this.state.draggable });
}
updatePosition = () => {
- const { lat, lng } = this.refs.marker.leafletElement.getLatLng()
+ const { lat, lng } = this.refs.marker.leafletElement.getLatLng();
this.setState({
marker: [ lat, lng ],
modified: true
@@ -71,8 +71,8 @@ class SelectionMap extends Component {
const map = this.getMap();
var options = {
fill: 'fill',
- showSubunits: true,
- }
+ showSubunits: true
+ };
var graphicScale = L.control.graphicScale([options]).addTo(map);
}
@@ -129,7 +129,7 @@ class SelectionMap extends Component {
}
- )
+ );
}
}
diff --git a/imports/ui/pages/FiresMap/FiresMap.js b/imports/ui/pages/FiresMap/FiresMap.js
index e7afd88..303eadf 100644
--- a/imports/ui/pages/FiresMap/FiresMap.js
+++ b/imports/ui/pages/FiresMap/FiresMap.js
@@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
import { Meteor } from 'meteor/meteor';
import { Trans, translate } from 'react-i18next';
import 'leaflet/dist/leaflet.css';
-import { Circle, CircleMarker, Map, Marker, Popup, TileLayer, PropTypes as MapPropTypes } from 'react-leaflet'
+import { Circle, CircleMarker, Map, Marker, Popup, TileLayer, PropTypes as MapPropTypes } from 'react-leaflet';
import ActiveFiresCollection from '../../../api/ActiveFires/ActiveFires';
import FireAlertsCollection from '../../../api/FireAlerts/FireAlerts';
import UserSubsToFiresCollection from '../../../api/Subscriptions/Subscriptions';
@@ -12,7 +12,7 @@ import CenterInMyPosition from '/imports/ui/components/CenterInMyPosition/Center
import { withTracker } from 'meteor/react-meteor-data';
import Loading from '../../components/Loading/Loading';
import './FiresMap.scss';
-import Leaflet from 'leaflet'
+import Leaflet from 'leaflet';
import LGeo from 'leaflet-geodesy';
import union from '@turf/union';
import 'leaflet-graphicscale/dist/Leaflet.GraphicScale.min.css';
@@ -36,7 +36,7 @@ const fireIcon = new Leaflet.Icon({
/* shadowUrl: require('../public/marker-shadow.png'), */
iconSize: [16, 24], // size of the icon
/* shadowSize: [50, 64], // size of the shadow */
- iconAnchor: [8, 26], // point of the icon which will correspond to marker's location
+ iconAnchor: [8, 26] // point of the icon which will correspond to marker's location
/* shadowAnchor: [4, 62], // the same for the shadow
* popupAnchor: [-3, -76]// point from which the popup should open relative to the iconAnchor*/
})
@@ -46,7 +46,7 @@ const nFireIcon = new Leaflet.Icon({
/* shadowUrl: require('../public/marker-shadow.png'), */
iconSize: [16, 24], // size of the icon
/* shadowSize: [50, 64], // size of the shadow */
- iconAnchor: [8, 26], // point of the icon which will correspond to marker's location
+ iconAnchor: [8, 26] // point of the icon which will correspond to marker's location
/* shadowAnchor: [4, 62], // the same for the shadow
* popupAnchor: [-3, -76]// point from which the popup should open relative to the iconAnchor*/
})
@@ -60,38 +60,38 @@ const MyPopupMarker = ({ children, lat, lon, nasa}) => (
-)
+);
const FireMark = ({ lat, lon, scan, nasa }) => (
-)
+);
/* Less acurate (1 pixel per fire) but faster */
const FirePixel = ({ lat, lon, nasa }) => (
-)
+);
MyPopupMarker.propTypes = {
// https://github.com/PaulLeCam/react-leaflet/tree/master/src/propTypes
children: MapPropTypes.children,
lat: PropTypes.number.isRequired,
lon: PropTypes.number.isRequired,
- nasa: PropTypes.bool.isRequired,
-}
+ nasa: PropTypes.bool.isRequired
+};
FirePixel.propTypes = {
lat: PropTypes.number.isRequired,
lon: PropTypes.number.isRequired,
- nasa: PropTypes.bool.isRequired,
-}
+ nasa: PropTypes.bool.isRequired
+};
FireMark.propTypes = {
scan: PropTypes.number.isRequired,
lat: PropTypes.number.isRequired,
lon: PropTypes.number.isRequired,
- nasa: PropTypes.bool.isRequired,
-}
+ nasa: PropTypes.bool.isRequired
+};
// Below this use only pixels
const MAXZOOM = 7;
@@ -99,9 +99,9 @@ const MAXZOOM = 7;
const MyMarkersList = ({ markers }) => {
const items = markers.map(({ key, ...props }) => (
- ))
- return {items}
-}
+ ));
+ return {items}
;
+};
const FireList = ({ fires, scale, useMarkers, nasa }) => {
/* if (nasa) {
@@ -112,20 +112,20 @@ const FireList = ({ fires, scale, useMarkers, nasa }) => {
* }*/
const items = fires.map(({ _id, ...props }) => (
useMarkers && !scale? :
- (!nasa && !scale)? :))
- return {items}
-}
+ (!nasa && !scale)? :));
+ return {items}
;
+};
MyMarkersList.propTypes = {
- markers: PropTypes.array.isRequired,
-}
+ markers: PropTypes.array.isRequired
+};
const DEF_LAT = 35.159028;
const DEF_LNG = -46.738057;
const DEFAULT_VIEWPORT = {
center: [DEF_LAT, DEF_LNG], // a point in the sea
- zoom: 8,
-}
+ zoom: 8
+};
class FiresMap extends React.Component {
@@ -136,13 +136,13 @@ class FiresMap extends React.Component {
modified: false,
useMarkers: false,
showSubsUnion: true
- }
+ };
this.unionGroup = new L.LayerGroup();
}
centerOnUserLocation = (viewport) => {
this.onViewportChanged(viewport);
- }
+ };
componentDidMount() {
height.set(this.divElement.clientHeight);
@@ -156,7 +156,9 @@ class FiresMap extends React.Component {
lng.set(viewport.center[1]);
self.state.viewport = viewport;
self.state.modified = true;
- self.showSubsUnion(self.state.showSubsUnion);
+ if (self.props.subsready && self.refs.fireMap) {
+ self.showSubsUnion(self.state.showSubsUnion);
+ }
}, 2000);
}
@@ -192,17 +194,17 @@ class FiresMap extends React.Component {
if (show) {
// http://leafletjs.com/reference-1.2.0.html#path
var copts = {
- parts: 144,
+ parts: 144
};
UserSubsToFiresCollection.find().forEach( function(subs){
- var circle = LGeo.circle([subs.lat, subs.lon], subs.distance * 1000, copts)
+ var circle = LGeo.circle([subs.lat, subs.lon], subs.distance * 1000, copts);
circle.addTo(unionGroup);
});
this.union = unify(unionGroup.getLayers());
this.union.setStyle({
color: "#145A32",
fillColor: "green",
- fillOpacity: .1,
+ fillOpacity: .1
});
this.union.addTo(map);
}
@@ -213,15 +215,15 @@ class FiresMap extends React.Component {
const map = this.getMap();
var options = {
fill: 'fill',
- showSubunits: true,
- }
+ showSubunits: true
+ };
var graphicScale = L.control.graphicScale([options]).addTo(map);
}
render() {
this.state.viewport = !this.state.modified && this.props.viewport && Array.isArray(this.props.viewport.center)? this.props.viewport: this.state.viewport;
- if (this.props.subsready && this.refs['fireMap']) {
+ if (this.props.subsready && this.refs.fireMap) {
// Show union of users
this.showSubsUnion(this.state.showSubsUnion);
};
@@ -240,12 +242,12 @@ class FiresMap extends React.Component {
Fuegos activos
-
+
{this.props.activefires.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.:En rojo, {{count: this.props.activefires.length}} fuegos activos en el mapa. Hay un total de {{countTotal: this.props.activefirestotal}} fuegos activos detectados en todo el mundo por la NASA.
}
-
- En naranja, los fuegos notificados por nuestros usuarios/as recientemente.
+
+ En naranja, los fuegos notificados por nuestros usuarios/as recientemente.
this.showSubsUnion(e.target.checked)}>
@@ -288,8 +290,8 @@ class FiresMap extends React.Component {
);
- }
-}
+ };
+};
const zoom = new ReactiveVar(8);
const lat = new ReactiveVar(DEF_LAT);
@@ -343,7 +345,7 @@ export default translate([], { wait: true }) (withTracker(() => {
userSubs: UserSubsToFiresCollection.find().fetch(),
viewport: {
center: [lat.get(), lng.get()], // a point in the sea
- zoom: zoom.get(),
+ zoom: zoom.get()
}
};
})(FiresMap));
diff --git a/imports/ui/pages/Sandbox/Sandbox.js b/imports/ui/pages/Sandbox/Sandbox.js
index 314c869..535bb86 100644
--- a/imports/ui/pages/Sandbox/Sandbox.js
+++ b/imports/ui/pages/Sandbox/Sandbox.js
@@ -26,7 +26,8 @@ class Sandbox extends React.Component {
- )
+ );
}
}
+
export default translate([], { wait: true }) (Sandbox);
diff --git a/package-lock.json b/package-lock.json
index ba5e84e..96dada4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -228,6 +228,11 @@
"sprintf-js": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz"
}
},
+ "argv": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/argv/-/argv-0.0.2.tgz",
+ "integrity": "sha1-7L0W+JSbFXGDcRsb2jNPN4QBhas="
+ },
"aria-query": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-0.7.0.tgz",
@@ -2384,6 +2389,212 @@
"version": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
},
+ "codecov": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/codecov/-/codecov-2.3.1.tgz",
+ "integrity": "sha1-fdqUXNWKH2CBAltbA+4Bou8g+G4=",
+ "requires": {
+ "argv": "0.0.2",
+ "request": "2.77.0",
+ "urlgrey": "0.4.4"
+ },
+ "dependencies": {
+ "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.11.0",
+ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz",
+ "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c="
+ },
+ "combined-stream": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz",
+ "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=",
+ "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.5",
+ "mime-types": "2.1.17"
+ }
+ },
+ "har-validator": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz",
+ "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=",
+ "requires": {
+ "chalk": "1.1.3",
+ "commander": "2.9.0",
+ "is-my-json-valid": "2.16.1",
+ "pinkie-promise": "2.0.1"
+ }
+ },
+ "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="
+ },
+ "pinkie": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+ "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA="
+ },
+ "pinkie-promise": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
+ "requires": {
+ "pinkie": "2.0.4"
+ }
+ },
+ "qs": {
+ "version": "6.3.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.3.2.tgz",
+ "integrity": "sha1-51vV9uJoEioqDgvaYwslUMFmUCw="
+ },
+ "request": {
+ "version": "2.77.0",
+ "resolved": "https://registry.npmjs.org/request/-/request-2.77.0.tgz",
+ "integrity": "sha1-KwDYIDDt7cyXCJ/6XYgQqcKqMUs=",
+ "requires": {
+ "aws-sign2": "0.6.0",
+ "aws4": "1.6.0",
+ "caseless": "0.11.0",
+ "combined-stream": "1.0.5",
+ "extend": "3.0.1",
+ "forever-agent": "0.6.1",
+ "form-data": "2.1.4",
+ "har-validator": "2.0.6",
+ "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",
+ "node-uuid": "1.4.8",
+ "oauth-sign": "0.8.2",
+ "qs": "6.3.2",
+ "stringstream": "0.0.5",
+ "tough-cookie": "2.3.3",
+ "tunnel-agent": "0.4.3"
+ }
+ },
+ "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.4.3",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz",
+ "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us="
+ }
+ }
+ },
"color-convert": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz",
@@ -4415,6 +4626,19 @@
}
}
},
+ "generate-function": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz",
+ "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ="
+ },
+ "generate-object-property": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz",
+ "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=",
+ "requires": {
+ "is-property": "1.0.2"
+ }
+ },
"geojson-area": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/geojson-area/-/geojson-area-0.0.1.tgz",
@@ -4726,6 +4950,50 @@
"resolved": "https://registry.npmjs.org/i18next-localstorage-cache/-/i18next-localstorage-cache-1.1.1.tgz",
"integrity": "sha1-V1JWzDXoyy2IFI91R2b90tJLsbc="
},
+ "i18next-sync-fs-backend": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/i18next-sync-fs-backend/-/i18next-sync-fs-backend-1.0.0.tgz",
+ "integrity": "sha512-fymETW6cOnpqf2tm14/CgpPn881SJYACD98lwvsflHKFPLEKm6S0e6atGCdQ37beizsiZJuGF02Bjx7yS/DAzg==",
+ "requires": {
+ "codecov": "2.3.1",
+ "js-yaml": "3.5.4",
+ "json5": "0.5.0"
+ },
+ "dependencies": {
+ "argparse": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz",
+ "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=",
+ "requires": {
+ "sprintf-js": "1.0.3"
+ }
+ },
+ "esprima": {
+ "version": "2.7.3",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz",
+ "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE="
+ },
+ "js-yaml": {
+ "version": "3.5.4",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.5.4.tgz",
+ "integrity": "sha1-9k8W3NeL65zoNhBo5zPr5HsHkXk=",
+ "requires": {
+ "argparse": "1.0.9",
+ "esprima": "2.7.3"
+ }
+ },
+ "json5": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.0.tgz",
+ "integrity": "sha1-myBxWwJsvjd4/Xae3M2CLYMypbI="
+ },
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
+ }
+ }
+ },
"i18next-xhr-backend": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/i18next-xhr-backend/-/i18next-xhr-backend-1.5.0.tgz",
@@ -4985,6 +5253,17 @@
"is-extglob": "1.0.0"
}
},
+ "is-my-json-valid": {
+ "version": "2.16.1",
+ "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.1.tgz",
+ "integrity": "sha512-ochPsqWS1WXj8ZnMIV0vnNXooaMhp7cyL4FMSIPKTtnV0Ha/T19G2b9kkhcNsabV9bxYkze7/aLZJb/bYuFduQ==",
+ "requires": {
+ "generate-function": "2.0.0",
+ "generate-object-property": "1.2.0",
+ "jsonpointer": "4.0.1",
+ "xtend": "4.0.1"
+ }
+ },
"is-number": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz",
@@ -5036,6 +5315,11 @@
"integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
"dev": true
},
+ "is-property": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
+ "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ="
+ },
"is-regex": {
"version": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
"integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
@@ -6964,6 +7248,11 @@
"integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=",
"dev": true
},
+ "jsonpointer": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz",
+ "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk="
+ },
"jsprim": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
@@ -8471,6 +8760,11 @@
}
}
},
+ "node-uuid": {
+ "version": "1.4.8",
+ "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz",
+ "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc="
+ },
"nodemailer": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-4.4.1.tgz",
@@ -11029,6 +11323,11 @@
"invariant": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz"
}
},
+ "urlgrey": {
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/urlgrey/-/urlgrey-0.4.4.tgz",
+ "integrity": "sha1-iS/pWWCAXoVRnxzUOJ8stMu3ZS8="
+ },
"util-deprecate": {
"version": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
diff --git a/package.json b/package.json
index c78877a..f3080d7 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,7 @@
"i18next": "^10.0.7",
"i18next-browser-languagedetector": "^2.1.0",
"i18next-localstorage-cache": "^1.1.1",
+ "i18next-sync-fs-backend": "^1.0.0",
"i18next-xhr-backend": "^1.5.0",
"immutability-helper": "^2.5.1",
"indexof": "0.0.1",
@@ -114,6 +115,10 @@
"error",
"except-parens"
],
+ "comma-dangle": [
+ "error",
+ "never"
+ ],
"no-underscore-dangle": [
"error",
{