Many improvements in SelectionMap
This commit is contained in:
parent
7a672bca87
commit
e3eddd6b85
13 changed files with 173 additions and 132 deletions
|
|
@ -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({
|
Subscriptions.schema = new SimpleSchema({
|
||||||
location: Object,
|
location: Object,
|
||||||
'location.lat': Number,
|
'location.lat': Number,
|
||||||
'location.lon': Number,
|
'location.lon': Number,
|
||||||
|
geo: LocationSchema,
|
||||||
distance: Number,
|
distance: Number,
|
||||||
owner: String,
|
owner: String,
|
||||||
type: String
|
type: String
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,13 @@ import { check, Match } from 'meteor/check';
|
||||||
import Subscriptions from './Subscriptions';
|
import Subscriptions from './Subscriptions';
|
||||||
import rateLimit from '../../modules/rate-limit';
|
import rateLimit from '../../modules/rate-limit';
|
||||||
|
|
||||||
|
function geo(doc) {
|
||||||
|
return {
|
||||||
|
type: 'Point',
|
||||||
|
coordinates: [doc.location.lon, doc.location.lat]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
Meteor.methods({
|
Meteor.methods({
|
||||||
'subscriptions.insert': function subscriptionsInsert(doc) {
|
'subscriptions.insert': function subscriptionsInsert(doc) {
|
||||||
check(doc, {
|
check(doc, {
|
||||||
|
|
@ -10,10 +17,17 @@ Meteor.methods({
|
||||||
distance: Number
|
distance: Number
|
||||||
});
|
});
|
||||||
const type = 'web';
|
const type = 'web';
|
||||||
|
|
||||||
try {
|
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) {
|
} catch (exception) {
|
||||||
|
console.error(exception);
|
||||||
throw new Meteor.Error('500', exception);
|
throw new Meteor.Error('500', exception);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -25,8 +39,10 @@ Meteor.methods({
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const dup = doc;
|
||||||
const subscriptionId = doc._id;
|
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.
|
return subscriptionId; // Return _id so we can redirect to subscription after update.
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
throw new Meteor.Error('500', exception);
|
throw new Meteor.Error('500', exception);
|
||||||
|
|
@ -38,6 +54,7 @@ Meteor.methods({
|
||||||
try {
|
try {
|
||||||
return Subscriptions.remove(subscriptionId);
|
return Subscriptions.remove(subscriptionId);
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
|
console.error(exception);
|
||||||
throw new Meteor.Error('500', exception);
|
throw new Meteor.Error('500', exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,13 +35,13 @@ Meteor.publishTransformed('userSubsToFires', function transform() {
|
||||||
}
|
}
|
||||||
// console.log(`with noise: [${doc.lat}, ${doc.lon}]`);
|
// console.log(`with noise: [${doc.lat}, ${doc.lon}]`);
|
||||||
delete doc.chatId;
|
delete doc.chatId;
|
||||||
delete doc.geo;
|
// delete doc.geo;
|
||||||
return doc;
|
return doc;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
Meteor.publish('mysubscriptions', function subscriptions() {
|
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.
|
// 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);
|
check(subscriptionId, String);
|
||||||
const id = new Mongo.ObjectID(subscriptionId);
|
const id = new Mongo.ObjectID(subscriptionId);
|
||||||
check(id, Meteor.Collection.ObjectID);
|
check(id, Meteor.Collection.ObjectID);
|
||||||
return Subscriptions.find({ _id: id, owner: this.userId });
|
return Subscriptions.find({ _id: id, owner: this.userId, type: 'web' });
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ class DistanceSlider extends React.Component {
|
||||||
<div className="dist-slider">
|
<div className="dist-slider">
|
||||||
<p><Trans parent="span">¿A que distancia a la redonda quieres recibir notificaciones?</Trans></p>
|
<p><Trans parent="span">¿A que distancia a la redonda quieres recibir notificaciones?</Trans></p>
|
||||||
<Slider
|
<Slider
|
||||||
min={5}
|
min={1}
|
||||||
max={105}
|
max={105}
|
||||||
value={this.state.value}
|
value={this.state.value}
|
||||||
trackStyle={{ backgroundColor: 'green', height: 8 }}
|
trackStyle={{ backgroundColor: 'green', height: 8 }}
|
||||||
|
|
@ -68,7 +68,8 @@ class DistanceSlider extends React.Component {
|
||||||
height: 8
|
height: 8
|
||||||
}}
|
}}
|
||||||
marks={{
|
marks={{
|
||||||
10: { label: '10км' },
|
1: { label: '1км' },
|
||||||
|
10: { label: '10' },
|
||||||
20: { label: '20' },
|
20: { label: '20' },
|
||||||
30: { label: '30' },
|
30: { label: '30' },
|
||||||
40: { label: '40' },
|
40: { label: '40' },
|
||||||
|
|
@ -90,7 +91,7 @@ class DistanceSlider extends React.Component {
|
||||||
onAfterChange={this.onAfterChange}
|
onAfterChange={this.onAfterChange}
|
||||||
onChange={this.onSliderChange}
|
onChange={this.onSliderChange}
|
||||||
defaultValue={10}
|
defaultValue={10}
|
||||||
step={5}
|
step={1}
|
||||||
handle={handle}
|
handle={handle}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -7,56 +7,8 @@ import PropTypes from 'prop-types';
|
||||||
import { Modal, Button } from 'react-bootstrap';
|
import { Modal, Button } from 'react-bootstrap';
|
||||||
import { confirmable } from 'react-confirm';
|
import { confirmable } from 'react-confirm';
|
||||||
|
|
||||||
|
// https://github.com/haradakunihiko/react-confirm/blob/master/example/react-bootstrap/src/components/Confirmation.js
|
||||||
class Prompt extends Component {
|
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 (
|
|
||||||
<div
|
|
||||||
className="modal"
|
|
||||||
tabIndex="-1"
|
|
||||||
role="dialog"
|
|
||||||
>
|
|
||||||
<div className="modal-dialog" role="document">
|
|
||||||
<div className="modal-content">
|
|
||||||
<div className="modal-header">
|
|
||||||
<h5 className="modal-title">Modal title</h5>
|
|
||||||
<button type="button" className="close" data-dismiss="modal" aria-label="Close">
|
|
||||||
<span aria-hidden="true">×</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="modal-body">
|
|
||||||
<p>Modal body text goes here.</p>
|
|
||||||
</div>
|
|
||||||
<div className="modal-footer">
|
|
||||||
<button type="button" className="btn btn-primary">Save changes</button>
|
|
||||||
<button type="button" className="btn btn-secondary" data-dismiss="modal">Close</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
show,
|
show,
|
||||||
|
|
@ -66,6 +18,7 @@ role="dialog"
|
||||||
confirmation,
|
confirmation,
|
||||||
okBtn,
|
okBtn,
|
||||||
cancelBtn,
|
cancelBtn,
|
||||||
|
title,
|
||||||
enableEscape = true
|
enableEscape = true
|
||||||
} = this.props;
|
} = this.props;
|
||||||
return (
|
return (
|
||||||
|
|
@ -78,9 +31,11 @@ role="dialog"
|
||||||
backdrop={enableEscape ? true : 'static'}
|
backdrop={enableEscape ? true : 'static'}
|
||||||
keyboard={enableEscape}
|
keyboard={enableEscape}
|
||||||
>
|
>
|
||||||
<Modal.Header closeButton>
|
{title &&
|
||||||
<Modal.Title id="ModalHeader" />
|
<Modal.Header closeButton>
|
||||||
</Modal.Header>
|
<Modal.Title id="ModalHeader">{title}</Modal.Title>
|
||||||
|
</Modal.Header>
|
||||||
|
}
|
||||||
<Modal.Body>
|
<Modal.Body>
|
||||||
<p>{confirmation}</p>
|
<p>{confirmation}</p>
|
||||||
</Modal.Body>
|
</Modal.Body>
|
||||||
|
|
@ -100,6 +55,7 @@ Prompt.propTypes = {
|
||||||
cancel: PropTypes.func, // from confirmable. call to close the dialog with promise rejected.
|
cancel: PropTypes.func, // from confirmable. call to close the dialog with promise rejected.
|
||||||
dismiss: PropTypes.func, // from confirmable. call to only close the dialog.
|
dismiss: PropTypes.func, // from confirmable. call to only close the dialog.
|
||||||
confirmation: PropTypes.string, // arguments of your confirm function
|
confirmation: PropTypes.string, // arguments of your confirm function
|
||||||
|
title: PropTypes.string,
|
||||||
okBtn: PropTypes.string.isRequired,
|
okBtn: PropTypes.string.isRequired,
|
||||||
cancelBtn: PropTypes.string.isRequired,
|
cancelBtn: PropTypes.string.isRequired,
|
||||||
enableEscape: PropTypes.bool
|
enableEscape: PropTypes.bool
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@
|
||||||
|
|
||||||
import React, { Component, Fragment } from 'react';
|
import React, { Component, Fragment } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
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 Leaflet from 'leaflet';
|
||||||
import { translate } from 'react-i18next';
|
import { translate } from 'react-i18next';
|
||||||
import { withTracker } from 'meteor/react-meteor-data';
|
import { withTracker } from 'meteor/react-meteor-data';
|
||||||
|
|
@ -20,6 +21,7 @@ import 'leaflet-sleep/Leaflet.Sleep.js';
|
||||||
import Control from 'react-leaflet-control';
|
import Control from 'react-leaflet-control';
|
||||||
import { Button, ButtonGroup } from 'react-bootstrap';
|
import { Button, ButtonGroup } from 'react-bootstrap';
|
||||||
import subsUnion from '/imports/ui/components/Maps/SubsUnion/SubsUnion';
|
import subsUnion from '/imports/ui/components/Maps/SubsUnion/SubsUnion';
|
||||||
|
import UserSubsToFiresCollection from '/imports/api/Subscriptions/Subscriptions';
|
||||||
import './SelectionMap.scss';
|
import './SelectionMap.scss';
|
||||||
|
|
||||||
export const action = {
|
export const action = {
|
||||||
|
|
@ -114,7 +116,12 @@ class SelectionMap extends Component {
|
||||||
if (this.props.currentSubs.length > 0 && this.state.subsFit) {
|
if (this.props.currentSubs.length > 0 && this.state.subsFit) {
|
||||||
// has autofit, do nothing
|
// has autofit, do nothing
|
||||||
} else if (this.selectionMap && this.distanceCircle) {
|
} 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() {
|
isValidState() {
|
||||||
return this.state.center && this.state.center[0];
|
return !this.props.loadingSubs && this.state.center && this.state.center[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLeafletLoad(map) {
|
handleLeafletLoad(map) {
|
||||||
|
|
@ -175,7 +182,7 @@ class SelectionMap extends Component {
|
||||||
>
|
>
|
||||||
<DefMapLayers gray={false} />
|
<DefMapLayers gray={false} />
|
||||||
{this.props.action === action.edit &&
|
{this.props.action === action.edit &&
|
||||||
this.props.currentSubs.map(subs => (
|
this.props.currentSubs.map((subs, index) => (
|
||||||
<Marker
|
<Marker
|
||||||
key={subs._id}
|
key={subs._id}
|
||||||
draggable={false}
|
draggable={false}
|
||||||
|
|
@ -183,7 +190,18 @@ class SelectionMap extends Component {
|
||||||
icon={removeIcon}
|
icon={removeIcon}
|
||||||
title={t('Pulsa para borrar')}
|
title={t('Pulsa para borrar')}
|
||||||
onClick={() => { onRemove(subs._id); }}
|
onClick={() => { onRemove(subs._id); }}
|
||||||
/>
|
>
|
||||||
|
{index === 0 &&
|
||||||
|
<Tooltip
|
||||||
|
permanent
|
||||||
|
direction="right"
|
||||||
|
/* Use .openTooltip(); in the future */
|
||||||
|
offset={[10, -10]}
|
||||||
|
>
|
||||||
|
<span>{t('Pulsa aquí para borrar la zona')}</span>
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
</Marker>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
{this.props.action === action.add &&
|
{this.props.action === action.add &&
|
||||||
|
|
@ -257,14 +275,20 @@ SelectionMap.propTypes = {
|
||||||
onSndBtn: PropTypes.func,
|
onSndBtn: PropTypes.func,
|
||||||
onRemove: PropTypes.func,
|
onRemove: PropTypes.func,
|
||||||
action: PropTypes.number.isRequired,
|
action: PropTypes.number.isRequired,
|
||||||
loadingSubs: PropTypes.bool,
|
loadingSubs: PropTypes.bool.isRequired,
|
||||||
currentSubs: PropTypes.arrayOf(PropTypes.shape({
|
currentSubs: PropTypes.arrayOf(PropTypes.shape({
|
||||||
location: PropTypes.shape({ latitude: PropTypes.number, longitude: PropTypes.number }).isRequired,
|
location: PropTypes.shape({ latitude: PropTypes.number, longitude: PropTypes.number }).isRequired,
|
||||||
distance: PropTypes.number.isRequired
|
distance: PropTypes.number.isRequired
|
||||||
}))
|
}))
|
||||||
};
|
};
|
||||||
|
|
||||||
export default translate([], { wait: true })(withTracker(props => ({
|
export default translate([], { wait: true })(withTracker((props) => {
|
||||||
center: props.center[0] !== null ? props.center : geolocation.get(),
|
const subscription = Meteor.subscribe('mysubscriptions');
|
||||||
distance: props.distance
|
// console.log(props.loadingSubs);
|
||||||
}))(SelectionMap));
|
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));
|
||||||
|
|
|
||||||
|
|
@ -30,16 +30,22 @@ class SubscriptionEditor extends React.Component {
|
||||||
|
|
||||||
if (existingSubscription) doc._id = existingSubscription;
|
if (existingSubscription) doc._id = existingSubscription;
|
||||||
|
|
||||||
Meteor.call(methodToCall, doc, (error, subscriptionId) => {
|
const authenticated = !!Meteor.userId();
|
||||||
if (error) {
|
|
||||||
Bert.alert(error.reason, 'danger');
|
if (authenticated) {
|
||||||
} else {
|
Meteor.call(methodToCall, doc, (error, subscriptionId) => {
|
||||||
const confirmation = existingSubscription ? t('Zona actualizada') : t('Zona añadida');
|
if (error) {
|
||||||
Bert.alert(confirmation, 'success');
|
Bert.alert(error.reason, 'danger');
|
||||||
// history.push(`/subscriptions/${subscriptionId}`);
|
} else {
|
||||||
history.push('/subscriptions');
|
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() {
|
render() {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
/* eslint-disable react/jsx-indent-props */
|
/* eslint-disable react/jsx-indent-props */
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Meteor } from 'meteor/meteor';
|
|
||||||
import { withTracker } from 'meteor/react-meteor-data';
|
import { withTracker } from 'meteor/react-meteor-data';
|
||||||
import { Row, Col } from 'react-bootstrap';
|
import { Row, Col } from 'react-bootstrap';
|
||||||
import { translate } from 'react-i18next';
|
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 SelectionMap, { action } from '/imports/ui/components/SelectionMap/SelectionMap';
|
||||||
import Gkeys from '/imports/startup/client/Gkeys';
|
import Gkeys from '/imports/startup/client/Gkeys';
|
||||||
import CenterInMyPosition from '/imports/ui/components/CenterInMyPosition/CenterInMyPosition.js';
|
import CenterInMyPosition from '/imports/ui/components/CenterInMyPosition/CenterInMyPosition.js';
|
||||||
import UserSubsToFiresCollection from '/imports/api/Subscriptions/Subscriptions';
|
|
||||||
import SubsAutocomplete from './SubsAutocomplete';
|
import SubsAutocomplete from './SubsAutocomplete';
|
||||||
|
|
||||||
class FireSubscription extends React.Component {
|
class FireSubscription extends React.Component {
|
||||||
|
|
@ -96,8 +94,6 @@ class FireSubscription extends React.Component {
|
||||||
onFstBtn={state => this.onSubs(state)}
|
onFstBtn={state => this.onSubs(state)}
|
||||||
onSelection={state => this.onSelection(state)}
|
onSelection={state => this.onSelection(state)}
|
||||||
action={action.add}
|
action={action.add}
|
||||||
loadingSubs={this.props.loading}
|
|
||||||
currentSubs={this.props.subscriptions}
|
|
||||||
/>
|
/>
|
||||||
</Row>
|
</Row>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -106,9 +102,6 @@ class FireSubscription extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
FireSubscription.propTypes = {
|
FireSubscription.propTypes = {
|
||||||
t: PropTypes.func.isRequired,
|
|
||||||
loading: PropTypes.bool.isRequired,
|
|
||||||
subscriptions: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
center: PropTypes.arrayOf(PropTypes.number),
|
center: PropTypes.arrayOf(PropTypes.number),
|
||||||
zoom: PropTypes.number,
|
zoom: PropTypes.number,
|
||||||
distance: PropTypes.number,
|
distance: PropTypes.number,
|
||||||
|
|
@ -117,11 +110,4 @@ FireSubscription.propTypes = {
|
||||||
onSubs: PropTypes.func.isRequired
|
onSubs: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default translate([], { wait: true })(withTracker(() => {
|
export default translate([], { wait: true })(FireSubscription);
|
||||||
const subscription = Meteor.subscribe('mysubscriptions');
|
|
||||||
// console.log(UserSubsToFiresCollection.find().fetch());
|
|
||||||
return {
|
|
||||||
loading: !subscription.ready(),
|
|
||||||
subscriptions: UserSubsToFiresCollection.find({ owner: Meteor.userId() }).fetch()
|
|
||||||
};
|
|
||||||
})(FireSubscription));
|
|
||||||
|
|
|
||||||
|
|
@ -219,7 +219,10 @@ class Index extends Component {
|
||||||
<Section className="">
|
<Section className="">
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<h4 className="page-header"><Trans parent="span">Suscríbete a alertas de fuegos</Trans></h4>
|
<h4 className="page-header"><Trans parent="span">Suscríbete a alertas de fuegos</Trans></h4>
|
||||||
<SubscriptionEditor history={this.props.history} focusInput={false} />
|
<SubscriptionEditor
|
||||||
|
focusInput={false}
|
||||||
|
history={this.props.history}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="overlay" />
|
<div className="overlay" />
|
||||||
</Section>
|
</Section>
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ class Login extends React.Component {
|
||||||
super(props);
|
super(props);
|
||||||
this.t = props.t;
|
this.t = props.t;
|
||||||
this.handleSubmit = this.handleSubmit.bind(this);
|
this.handleSubmit = this.handleSubmit.bind(this);
|
||||||
console.log(this.props.location.state);
|
// console.log(this.props.location.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,10 @@ import { T9n } from 'meteor-accounts-t9n';
|
||||||
class Signup extends React.Component {
|
class Signup extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.t = this.props.t;
|
this.t = props.t;
|
||||||
this.handleSubmit = this.handleSubmit.bind(this);
|
this.handleSubmit = this.handleSubmit.bind(this);
|
||||||
|
// console.log(props.location.state);
|
||||||
|
this.state = props.location.state;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
|
@ -27,37 +29,37 @@ class Signup extends React.Component {
|
||||||
validate(component.form, {
|
validate(component.form, {
|
||||||
rules: {
|
rules: {
|
||||||
firstName: {
|
firstName: {
|
||||||
required: true,
|
required: true
|
||||||
},
|
},
|
||||||
lastName: {
|
lastName: {
|
||||||
required: true,
|
required: true
|
||||||
},
|
},
|
||||||
emailAddress: {
|
emailAddress: {
|
||||||
required: true,
|
required: true,
|
||||||
email: true,
|
email: true
|
||||||
},
|
},
|
||||||
password: {
|
password: {
|
||||||
required: true,
|
required: true,
|
||||||
minlength: 6,
|
minlength: 6
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
messages: {
|
messages: {
|
||||||
firstName: {
|
firstName: {
|
||||||
required: this.t("¿Cuál es tu nombre?"),
|
required: this.t('¿Cuál es tu nombre?')
|
||||||
},
|
},
|
||||||
lastName: {
|
lastName: {
|
||||||
required: this.t("¿Cuál es tu apellido?"),
|
required: this.t('¿Cuál es tu apellido?')
|
||||||
},
|
},
|
||||||
emailAddress: {
|
emailAddress: {
|
||||||
required: this.t("Necesitamos una contraseña aquí."),
|
required: this.t('Necesitamos una contraseña aquí.'),
|
||||||
email: this.t("¿Es correcto este correo?"),
|
email: this.t('¿Es correcto este correo?')
|
||||||
},
|
},
|
||||||
password: {
|
password: {
|
||||||
required: this.t("Necesitamos una contraseña aquí."),
|
required: this.t('Necesitamos una contraseña aquí.'),
|
||||||
minlength: this.t("Usa al menos seis caracteres."),
|
minlength: this.t('Usa al menos seis caracteres.')
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
submitHandler() { component.handleSubmit(); },
|
submitHandler() { component.handleSubmit(); }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,16 +72,16 @@ class Signup extends React.Component {
|
||||||
profile: {
|
profile: {
|
||||||
name: {
|
name: {
|
||||||
first: this.firstName.value,
|
first: this.firstName.value,
|
||||||
last: this.lastName.value,
|
last: this.lastName.value
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
}, (error) => {
|
}, (error) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
Bert.alert(T9n.get(`error.accounts.${error.reason}`), 'danger');
|
Bert.alert(T9n.get(`error.accounts.${error.reason}`), 'danger');
|
||||||
} else {
|
} else {
|
||||||
Meteor.call('users.sendVerificationEmail');
|
Meteor.call('users.sendVerificationEmail');
|
||||||
Bert.alert('Welcome!', 'success');
|
Bert.alert('Welcome!', 'success');
|
||||||
history.push('/documents');
|
history.push('/subscriptions');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -88,7 +90,7 @@ class Signup extends React.Component {
|
||||||
return (<div className="Signup">
|
return (<div className="Signup">
|
||||||
<Row className="align-items-center justify-content-center">
|
<Row className="align-items-center justify-content-center">
|
||||||
<Col xs={12} sm={6} md={5} lg={4}>
|
<Col xs={12} sm={6} md={5} lg={4}>
|
||||||
<h4 className="page-header">{this.t("Registrarse")}</h4>
|
<h4 className="page-header">{this.t('Registrarse')}</h4>
|
||||||
<Row>
|
<Row>
|
||||||
{/* <Col xs={12}>
|
{/* <Col xs={12}>
|
||||||
<button
|
<button
|
||||||
|
|
@ -102,7 +104,7 @@ class Signup extends React.Component {
|
||||||
services={['telegram', 'google']}
|
services={['telegram', 'google']}
|
||||||
emailMessage={{
|
emailMessage={{
|
||||||
offset: 97,
|
offset: 97,
|
||||||
text: this.t('o regístrate con un correo'),
|
text: this.t('o regístrate con un correo')
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
@ -111,7 +113,7 @@ class Signup extends React.Component {
|
||||||
<Row>
|
<Row>
|
||||||
<Col xs={6}>
|
<Col xs={6}>
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<ControlLabel>{this.t("Nombre")}</ControlLabel>
|
<ControlLabel>{this.t('Nombre')}</ControlLabel>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="firstName"
|
name="firstName"
|
||||||
|
|
@ -122,7 +124,7 @@ class Signup extends React.Component {
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={6}>
|
<Col xs={6}>
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<ControlLabel>{this.t("Apellidos")}</ControlLabel>
|
<ControlLabel>{this.t('Apellidos')}</ControlLabel>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="lastName"
|
name="lastName"
|
||||||
|
|
@ -133,7 +135,7 @@ class Signup extends React.Component {
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<ControlLabel>{this.t("Correo electrónico")}</ControlLabel>
|
<ControlLabel>{this.t('Correo electrónico')}</ControlLabel>
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
name="emailAddress"
|
name="emailAddress"
|
||||||
|
|
@ -142,18 +144,18 @@ class Signup extends React.Component {
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<ControlLabel>{this.t("Contraseña")}</ControlLabel>
|
<ControlLabel>{this.t('Contraseña')}</ControlLabel>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
name="password"
|
name="password"
|
||||||
ref={password => (this.password = password)}
|
ref={password => (this.password = password)}
|
||||||
className="form-control"
|
className="form-control"
|
||||||
/>
|
/>
|
||||||
<InputHint>{this.t("Usa al menos seis caracteres.")}</InputHint>
|
<InputHint>{this.t('Usa al menos seis caracteres.')}</InputHint>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<Button type="submit" bsStyle="success">{this.t("Registrarse")}</Button>
|
<Button type="submit" bsStyle="success">{this.t('Registrarse')}</Button>
|
||||||
<AccountPageFooter>
|
<AccountPageFooter>
|
||||||
<p>{this.t("¿Ya tienes un cuenta?")} <Link to="/login">{this.t("Iniciar sesión")}</Link>.</p>
|
<p>{this.t('¿Ya tienes un cuenta?')} <Link to={{ pathname: '/login', state: this.state }} >{this.t('Iniciar sesión')}</Link>.</p>
|
||||||
</AccountPageFooter>
|
</AccountPageFooter>
|
||||||
</form>
|
</form>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,11 @@ class Subscriptions extends Component {
|
||||||
|
|
||||||
onFstBtn() {
|
onFstBtn() {
|
||||||
// console.log(this.state);
|
// 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() {
|
onSndBtn() {
|
||||||
|
|
@ -42,15 +46,19 @@ class Subscriptions extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRemove(subscriptionId) {
|
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(
|
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
|
// `proceed` callback
|
||||||
|
const num = subscriptions.length;
|
||||||
Meteor.call('subscriptions.remove', subscriptionId, (error) => {
|
Meteor.call('subscriptions.remove', subscriptionId, (error) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
Bert.alert(error.reason, 'danger');
|
Bert.alert(error.reason, 'danger');
|
||||||
} else {
|
} else {
|
||||||
Bert.alert('Subscription deleted!', 'success');
|
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,
|
t,
|
||||||
subscriptions
|
subscriptions
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
const firstBtnTitle = ['Añadir zona', '', 'Terminar']; // view, add, edit
|
||||||
return (!loading ? (
|
return (!loading ? (
|
||||||
<div className="Subscriptions">
|
<div className="Subscriptions">
|
||||||
<div className="page-header clearfix">
|
<div className="page-header clearfix">
|
||||||
<h4 className="pull-left"><Trans>Suscripciones a fuegos en zonas de mi interés</Trans></h4>
|
<h4 className="pull-left"><Trans>Suscripciones a alertas de fuegos en zonas de mi interés</Trans></h4>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
{ subscriptions.length === 0 &&
|
{ subscriptions.length === 0 ?
|
||||||
<Alert bsStyle="warning"><Trans>No estás suscrito a fuegos en ninguna zona</Trans></Alert>
|
<Alert bsStyle="warning"><Trans>No estás suscrito a fuegos en ninguna zona</Trans></Alert> :
|
||||||
|
<Alert bsStyle="success"><Trans>En verde, áreas de las que recibirás alertas de fuegos</Trans></Alert>
|
||||||
}
|
}
|
||||||
<br />
|
<br />
|
||||||
<SelectionMap
|
<SelectionMap
|
||||||
center={[null, null]}
|
center={[null, null]}
|
||||||
zoom={11}
|
zoom={11}
|
||||||
action={this.state.action}
|
action={this.state.action}
|
||||||
fstBtn={t('Añadir zona')}
|
fstBtn={t(firstBtnTitle[this.state.action])}
|
||||||
onFstBtn={state => this.onFstBtn(state)}
|
onFstBtn={state => 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()}
|
onSndBtn={() => this.onSndBtn()}
|
||||||
onViewportChanged={viewport => this.onViewportChanged(viewport)}
|
onViewportChanged={viewport => this.onViewportChanged(viewport)}
|
||||||
loadingSubs={this.props.loading}
|
loadingSubs={this.props.loading}
|
||||||
|
|
|
||||||
|
|
@ -19,3 +19,7 @@ body {
|
||||||
.navbar-nav .nav-link {
|
.navbar-nav .nav-link {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal-dialog {
|
||||||
|
margin-top: 70px;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue