diff --git a/imports/api/Subscriptions/Subscriptions.js b/imports/api/Subscriptions/Subscriptions.js
index 373c015..b039ba1 100644
--- a/imports/api/Subscriptions/Subscriptions.js
+++ b/imports/api/Subscriptions/Subscriptions.js
@@ -37,10 +37,42 @@ Subscriptions.deny({
* }
* */
+// https://stackoverflow.com/questions/24492333/meteor-simple-schema-for-mongo-geo-location-data
+// https://github.com/aldeed/meteor-simple-schema/issues/606
+const LocationSchema = new SimpleSchema({
+ type: {
+ type: String,
+ allowedValues: ['Point']
+ },
+ coordinates: {
+ type: Array,
+ minCount: 2,
+ maxCount: 2,
+ custom: function custom() {
+ if (!(this.value[0] >= -90 && this.value[0] <= 90)) {
+ return 'lngOutOfRange';
+ }
+ if (!(this.value[1] >= -180 && this.value[1] <= 180)) {
+ return 'latOutOfRange';
+ }
+ return true;
+ }
+ },
+ 'coordinates.$': {
+ type: Number
+ }
+});
+
+LocationSchema.messageBox.messages({
+ lonOutOfRange: '[label] longitude should be between -90 and 90',
+ latOutOfRange: '[label] latitude should be between -180 and 180'
+});
+
Subscriptions.schema = new SimpleSchema({
location: Object,
'location.lat': Number,
'location.lon': Number,
+ geo: LocationSchema,
distance: Number,
owner: String,
type: String
diff --git a/imports/api/Subscriptions/methods.js b/imports/api/Subscriptions/methods.js
index d5e7c62..e7e3bef 100644
--- a/imports/api/Subscriptions/methods.js
+++ b/imports/api/Subscriptions/methods.js
@@ -3,6 +3,13 @@ import { check, Match } from 'meteor/check';
import Subscriptions from './Subscriptions';
import rateLimit from '../../modules/rate-limit';
+function geo(doc) {
+ return {
+ type: 'Point',
+ coordinates: [doc.location.lon, doc.location.lat]
+ };
+}
+
Meteor.methods({
'subscriptions.insert': function subscriptionsInsert(doc) {
check(doc, {
@@ -10,10 +17,17 @@ Meteor.methods({
distance: Number
});
const type = 'web';
-
try {
- return Subscriptions.insert({ owner: this.userId, type, ...doc });
+ const newDoc = {
+ owner: this.userId,
+ type,
+ geo: geo(doc),
+ ...doc
+ };
+ // console.log(newDoc);
+ return Subscriptions.insert(newDoc);
} catch (exception) {
+ console.error(exception);
throw new Meteor.Error('500', exception);
}
},
@@ -25,8 +39,10 @@ Meteor.methods({
});
try {
+ const dup = doc;
const subscriptionId = doc._id;
- Subscriptions.update(subscriptionId, { $set: doc });
+ dup.geo = geo(doc);
+ Subscriptions.update(subscriptionId, { $set: dup });
return subscriptionId; // Return _id so we can redirect to subscription after update.
} catch (exception) {
throw new Meteor.Error('500', exception);
@@ -38,6 +54,7 @@ Meteor.methods({
try {
return Subscriptions.remove(subscriptionId);
} catch (exception) {
+ console.error(exception);
throw new Meteor.Error('500', exception);
}
}
diff --git a/imports/api/Subscriptions/server/publications.js b/imports/api/Subscriptions/server/publications.js
index d3d9ed8..e711c0d 100644
--- a/imports/api/Subscriptions/server/publications.js
+++ b/imports/api/Subscriptions/server/publications.js
@@ -35,13 +35,13 @@ Meteor.publishTransformed('userSubsToFires', function transform() {
}
// console.log(`with noise: [${doc.lat}, ${doc.lon}]`);
delete doc.chatId;
- delete doc.geo;
+ // delete doc.geo;
return doc;
});
});
Meteor.publish('mysubscriptions', function subscriptions() {
- return Subscriptions.find({ owner: this.userId });
+ return Subscriptions.find({ owner: this.userId, type: 'web' });
});
// Note: subscriptions.view is also used when editing an existing subscription.
@@ -49,5 +49,5 @@ Meteor.publish('subscriptions.view', function subscriptionsView(subscriptionId)
check(subscriptionId, String);
const id = new Mongo.ObjectID(subscriptionId);
check(id, Meteor.Collection.ObjectID);
- return Subscriptions.find({ _id: id, owner: this.userId });
+ return Subscriptions.find({ _id: id, owner: this.userId, type: 'web' });
});
diff --git a/imports/ui/components/DistanceSlider/DistanceSlider.js b/imports/ui/components/DistanceSlider/DistanceSlider.js
index 417ae1e..3647c2e 100644
--- a/imports/ui/components/DistanceSlider/DistanceSlider.js
+++ b/imports/ui/components/DistanceSlider/DistanceSlider.js
@@ -56,7 +56,7 @@ class DistanceSlider extends React.Component {
¿A que distancia a la redonda quieres recibir notificaciones?
diff --git a/imports/ui/components/Prompt/Prompt.js b/imports/ui/components/Prompt/Prompt.js
index 1b39ebb..d5db620 100644
--- a/imports/ui/components/Prompt/Prompt.js
+++ b/imports/ui/components/Prompt/Prompt.js
@@ -7,56 +7,8 @@ import PropTypes from 'prop-types';
import { Modal, Button } from 'react-bootstrap';
import { confirmable } from 'react-confirm';
+// https://github.com/haradakunihiko/react-confirm/blob/master/example/react-bootstrap/src/components/Confirmation.js
class Prompt extends Component {
- // constructor(props) {
- /* super(props);
- * this.state = {
- * open: true
- * };
- }
-
- componentDidMount() {
- * console.log(this.prompt);
- * this.setState({ open: true });
- }
- */
-
- render2() {
- const {
- show,
- proceed,
- dismiss,
- cancel,
- confirmation,
- okBtn,
- cancelBtn
- } = this.props;
- return (
-
-
-
-
-
Modal title
-
-
-
-
Modal body text goes here.
-
-
-
-
-
-
-
-
- );
- }
render() {
const {
show,
@@ -66,6 +18,7 @@ role="dialog"
confirmation,
okBtn,
cancelBtn,
+ title,
enableEscape = true
} = this.props;
return (
@@ -78,9 +31,11 @@ role="dialog"
backdrop={enableEscape ? true : 'static'}
keyboard={enableEscape}
>
-
-
-
+ {title &&
+
+
+
+ }
{confirmation}
@@ -100,6 +55,7 @@ Prompt.propTypes = {
cancel: PropTypes.func, // from confirmable. call to close the dialog with promise rejected.
dismiss: PropTypes.func, // from confirmable. call to only close the dialog.
confirmation: PropTypes.string, // arguments of your confirm function
+ title: PropTypes.string,
okBtn: PropTypes.string.isRequired,
cancelBtn: PropTypes.string.isRequired,
enableEscape: PropTypes.bool
diff --git a/imports/ui/components/SelectionMap/SelectionMap.js b/imports/ui/components/SelectionMap/SelectionMap.js
index 940bd83..5be19d6 100644
--- a/imports/ui/components/SelectionMap/SelectionMap.js
+++ b/imports/ui/components/SelectionMap/SelectionMap.js
@@ -6,7 +6,8 @@
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
-import { Map, Marker, CircleMarker, Circle } from 'react-leaflet';
+import { Meteor } from 'meteor/meteor';
+import { Map, Marker, CircleMarker, Circle, Tooltip } from 'react-leaflet';
import Leaflet from 'leaflet';
import { translate } from 'react-i18next';
import { withTracker } from 'meteor/react-meteor-data';
@@ -20,6 +21,7 @@ import 'leaflet-sleep/Leaflet.Sleep.js';
import Control from 'react-leaflet-control';
import { Button, ButtonGroup } from 'react-bootstrap';
import subsUnion from '/imports/ui/components/Maps/SubsUnion/SubsUnion';
+import UserSubsToFiresCollection from '/imports/api/Subscriptions/Subscriptions';
import './SelectionMap.scss';
export const action = {
@@ -114,7 +116,12 @@ class SelectionMap extends Component {
if (this.props.currentSubs.length > 0 && this.state.subsFit) {
// has autofit, do nothing
} else if (this.selectionMap && this.distanceCircle) {
- this.getMap().fitBounds(this.distanceCircle.leafletElement.getBounds(), [70, 70]);
+ if (!this.getMap().getBounds().contains(this.distanceCircle.leafletElement.getBounds())) {
+ // console.log('New area circle not visible');
+ this.getMap().fitBounds(this.distanceCircle.leafletElement.getBounds()); // padding , [70, 70]);
+ } else {
+ // console.log('New area circle visible');
+ }
}
}
@@ -132,7 +139,7 @@ class SelectionMap extends Component {
}
isValidState() {
- return this.state.center && this.state.center[0];
+ return !this.props.loadingSubs && this.state.center && this.state.center[0];
}
handleLeafletLoad(map) {
@@ -175,7 +182,7 @@ class SelectionMap extends Component {
>
{this.props.action === action.edit &&
- this.props.currentSubs.map(subs => (
+ this.props.currentSubs.map((subs, index) => (
{ onRemove(subs._id); }}
- />
+ >
+ {index === 0 &&
+
+ {t('Pulsa aquí para borrar la zona')}
+
+ }
+
))
}
{this.props.action === action.add &&
@@ -257,14 +275,20 @@ SelectionMap.propTypes = {
onSndBtn: PropTypes.func,
onRemove: PropTypes.func,
action: PropTypes.number.isRequired,
- loadingSubs: PropTypes.bool,
+ loadingSubs: PropTypes.bool.isRequired,
currentSubs: PropTypes.arrayOf(PropTypes.shape({
location: PropTypes.shape({ latitude: PropTypes.number, longitude: PropTypes.number }).isRequired,
distance: PropTypes.number.isRequired
}))
};
-export default translate([], { wait: true })(withTracker(props => ({
- center: props.center[0] !== null ? props.center : geolocation.get(),
- distance: props.distance
-}))(SelectionMap));
+export default translate([], { wait: true })(withTracker((props) => {
+ const subscription = Meteor.subscribe('mysubscriptions');
+ // console.log(props.loadingSubs);
+ return {
+ center: props.center[0] !== null ? props.center : geolocation.get(),
+ distance: props.distance,
+ loadingSubs: !subscription.ready(),
+ currentSubs: UserSubsToFiresCollection.find({ owner: Meteor.userId(), type: 'web' }).fetch()
+ };
+})(SelectionMap));
diff --git a/imports/ui/components/SubscriptionEditor/SubscriptionEditor.js b/imports/ui/components/SubscriptionEditor/SubscriptionEditor.js
index 2a52314..f391402 100644
--- a/imports/ui/components/SubscriptionEditor/SubscriptionEditor.js
+++ b/imports/ui/components/SubscriptionEditor/SubscriptionEditor.js
@@ -30,16 +30,22 @@ class SubscriptionEditor extends React.Component {
if (existingSubscription) doc._id = existingSubscription;
- Meteor.call(methodToCall, doc, (error, subscriptionId) => {
- if (error) {
- Bert.alert(error.reason, 'danger');
- } else {
- const confirmation = existingSubscription ? t('Zona actualizada') : t('Zona añadida');
- Bert.alert(confirmation, 'success');
- // history.push(`/subscriptions/${subscriptionId}`);
- history.push('/subscriptions');
- }
- });
+ const authenticated = !!Meteor.userId();
+
+ if (authenticated) {
+ Meteor.call(methodToCall, doc, (error, subscriptionId) => {
+ if (error) {
+ Bert.alert(error.reason, 'danger');
+ } else {
+ const confirmation = existingSubscription ? t('Zona actualizada') : t('Zona añadida');
+ Bert.alert(confirmation, 'success');
+ // history.push(`/subscriptions/${subscriptionId}`);
+ history.push('/subscriptions');
+ }
+ });
+ } else {
+ this.props.history.push('/signup', doc);
+ }
}
render() {
diff --git a/imports/ui/pages/FireSubscription/FireSubscription.js b/imports/ui/pages/FireSubscription/FireSubscription.js
index 050be09..00f5d96 100644
--- a/imports/ui/pages/FireSubscription/FireSubscription.js
+++ b/imports/ui/pages/FireSubscription/FireSubscription.js
@@ -2,7 +2,6 @@
/* eslint-disable react/jsx-indent-props */
import React from 'react';
import PropTypes from 'prop-types';
-import { Meteor } from 'meteor/meteor';
import { withTracker } from 'meteor/react-meteor-data';
import { Row, Col } from 'react-bootstrap';
import { translate } from 'react-i18next';
@@ -10,7 +9,6 @@ import DistanceSlider from '/imports/ui/components/DistanceSlider/DistanceSlider
import SelectionMap, { action } from '/imports/ui/components/SelectionMap/SelectionMap';
import Gkeys from '/imports/startup/client/Gkeys';
import CenterInMyPosition from '/imports/ui/components/CenterInMyPosition/CenterInMyPosition.js';
-import UserSubsToFiresCollection from '/imports/api/Subscriptions/Subscriptions';
import SubsAutocomplete from './SubsAutocomplete';
class FireSubscription extends React.Component {
@@ -96,8 +94,6 @@ class FireSubscription extends React.Component {
onFstBtn={state => this.onSubs(state)}
onSelection={state => this.onSelection(state)}
action={action.add}
- loadingSubs={this.props.loading}
- currentSubs={this.props.subscriptions}
/>
@@ -106,9 +102,6 @@ class FireSubscription extends React.Component {
}
FireSubscription.propTypes = {
- t: PropTypes.func.isRequired,
- loading: PropTypes.bool.isRequired,
- subscriptions: PropTypes.arrayOf(PropTypes.object).isRequired,
center: PropTypes.arrayOf(PropTypes.number),
zoom: PropTypes.number,
distance: PropTypes.number,
@@ -117,11 +110,4 @@ FireSubscription.propTypes = {
onSubs: PropTypes.func.isRequired
};
-export default translate([], { wait: true })(withTracker(() => {
- const subscription = Meteor.subscribe('mysubscriptions');
- // console.log(UserSubsToFiresCollection.find().fetch());
- return {
- loading: !subscription.ready(),
- subscriptions: UserSubsToFiresCollection.find({ owner: Meteor.userId() }).fetch()
- };
-})(FireSubscription));
+export default translate([], { wait: true })(FireSubscription);
diff --git a/imports/ui/pages/Index/Index.js b/imports/ui/pages/Index/Index.js
index b94886e..6a775d6 100644
--- a/imports/ui/pages/Index/Index.js
+++ b/imports/ui/pages/Index/Index.js
@@ -219,7 +219,10 @@ class Index extends Component {
Suscríbete a alertas de fuegos
-
+
diff --git a/imports/ui/pages/Login/Login.js b/imports/ui/pages/Login/Login.js
index 4d446c7..8c004fb 100644
--- a/imports/ui/pages/Login/Login.js
+++ b/imports/ui/pages/Login/Login.js
@@ -16,7 +16,7 @@ class Login extends React.Component {
super(props);
this.t = props.t;
this.handleSubmit = this.handleSubmit.bind(this);
- console.log(this.props.location.state);
+ // console.log(this.props.location.state);
}
componentDidMount() {
diff --git a/imports/ui/pages/Signup/Signup.js b/imports/ui/pages/Signup/Signup.js
index 33a8e54..bb34baa 100644
--- a/imports/ui/pages/Signup/Signup.js
+++ b/imports/ui/pages/Signup/Signup.js
@@ -17,8 +17,10 @@ import { T9n } from 'meteor-accounts-t9n';
class Signup extends React.Component {
constructor(props) {
super(props);
- this.t = this.props.t;
+ this.t = props.t;
this.handleSubmit = this.handleSubmit.bind(this);
+ // console.log(props.location.state);
+ this.state = props.location.state;
}
componentDidMount() {
@@ -27,37 +29,37 @@ class Signup extends React.Component {
validate(component.form, {
rules: {
firstName: {
- required: true,
+ required: true
},
lastName: {
- required: true,
+ required: true
},
emailAddress: {
required: true,
- email: true,
+ email: true
},
password: {
required: true,
- minlength: 6,
- },
+ minlength: 6
+ }
},
messages: {
firstName: {
- required: this.t("¿Cuál es tu nombre?"),
+ required: this.t('¿Cuál es tu nombre?')
},
lastName: {
- required: this.t("¿Cuál es tu apellido?"),
+ required: this.t('¿Cuál es tu apellido?')
},
emailAddress: {
- required: this.t("Necesitamos una contraseña aquí."),
- email: this.t("¿Es correcto este correo?"),
+ required: this.t('Necesitamos una contraseña aquí.'),
+ email: this.t('¿Es correcto este correo?')
},
password: {
- required: this.t("Necesitamos una contraseña aquí."),
- minlength: this.t("Usa al menos seis caracteres."),
- },
+ required: this.t('Necesitamos una contraseña aquí.'),
+ minlength: this.t('Usa al menos seis caracteres.')
+ }
},
- submitHandler() { component.handleSubmit(); },
+ submitHandler() { component.handleSubmit(); }
});
}
@@ -70,16 +72,16 @@ class Signup extends React.Component {
profile: {
name: {
first: this.firstName.value,
- last: this.lastName.value,
- },
- },
+ last: this.lastName.value
+ }
+ }
}, (error) => {
if (error) {
Bert.alert(T9n.get(`error.accounts.${error.reason}`), 'danger');
} else {
Meteor.call('users.sendVerificationEmail');
Bert.alert('Welcome!', 'success');
- history.push('/documents');
+ history.push('/subscriptions');
}
});
}
@@ -88,7 +90,7 @@ class Signup extends React.Component {
return (
- {this.t("Registrarse")}
+ {this.t('Registrarse')}
{/*
@@ -111,7 +113,7 @@ class Signup extends React.Component {
- {this.t("Nombre")}
+ {this.t('Nombre')}
- {this.t("Apellidos")}
+ {this.t('Apellidos')}
- {this.t("Correo electrónico")}
+ {this.t('Correo electrónico')}
- {this.t("Contraseña")}
+ {this.t('Contraseña')}
(this.password = password)}
className="form-control"
/>
- {this.t("Usa al menos seis caracteres.")}
+ {this.t('Usa al menos seis caracteres.')}
-
+
- {this.t("¿Ya tienes un cuenta?")} {this.t("Iniciar sesión")}.
+ {this.t('¿Ya tienes un cuenta?')} {this.t('Iniciar sesión')}.
diff --git a/imports/ui/pages/Subscriptions/Subscriptions.js b/imports/ui/pages/Subscriptions/Subscriptions.js
index 704d611..bfed714 100644
--- a/imports/ui/pages/Subscriptions/Subscriptions.js
+++ b/imports/ui/pages/Subscriptions/Subscriptions.js
@@ -29,7 +29,11 @@ class Subscriptions extends Component {
onFstBtn() {
// console.log(this.state);
- this.props.history.push(`${this.props.match.url}/new`, { center: this.state.center, zoom: this.state.zoom });
+ if (this.state.action === action.view) {
+ this.props.history.push(`${this.props.match.url}/new`, { center: this.state.center, zoom: this.state.zoom });
+ } else if (this.state.action === action.edit) {
+ this.setState({ action: action.view });
+ }
}
onSndBtn() {
@@ -42,15 +46,19 @@ class Subscriptions extends Component {
}
handleRemove(subscriptionId) {
- const { t } = this.props;
+ const { t, subscriptions } = this.props;
confirm(t('Dejarás de recibir notificaciones de fuegos en esa área ¿Estás seguro/a? '), { okBtn: t('Sí'), cancelBtn: t('No') }).then(
() => {
// `proceed` callback
+ const num = subscriptions.length;
Meteor.call('subscriptions.remove', subscriptionId, (error) => {
if (error) {
Bert.alert(error.reason, 'danger');
} else {
Bert.alert('Subscription deleted!', 'success');
+ if (num === 1) { // it was 1, now deleted
+ this.setState({ action: action.view });
+ }
}
});
},
@@ -66,23 +74,25 @@ class Subscriptions extends Component {
t,
subscriptions
} = this.props;
+ const firstBtnTitle = ['Añadir zona', '', 'Terminar']; // view, add, edit
return (!loading ? (
-
Suscripciones a fuegos en zonas de mi interés
+ Suscripciones a alertas de fuegos en zonas de mi interés
- { subscriptions.length === 0 &&
-
No estás suscrito a fuegos en ninguna zona
+ { subscriptions.length === 0 ?
+
No estás suscrito a fuegos en ninguna zona :
+
En verde, áreas de las que recibirás alertas de fuegos
}
this.onFstBtn(state)}
- sndBtn={this.props.subscriptions.length >= 1 ? t('Editar') : null}
+ sndBtn={this.state.action === action.view && this.props.subscriptions.length >= 1 ? t('Editar') : null}
onSndBtn={() => this.onSndBtn()}
onViewportChanged={viewport => this.onViewportChanged(viewport)}
loadingSubs={this.props.loading}
diff --git a/imports/ui/stylesheets/bootstrap-overrides.scss b/imports/ui/stylesheets/bootstrap-overrides.scss
index 3506bc4..7016a34 100644
--- a/imports/ui/stylesheets/bootstrap-overrides.scss
+++ b/imports/ui/stylesheets/bootstrap-overrides.scss
@@ -19,3 +19,7 @@ body {
.navbar-nav .nav-link {
white-space: nowrap;
}
+
+.modal-dialog {
+ margin-top: 70px;
+}