More work with subscriptions

This commit is contained in:
vjrj 2017-12-18 03:16:06 +01:00
parent 918103a17b
commit 497724443f
11 changed files with 164 additions and 62 deletions

View file

@ -37,7 +37,7 @@ Meteor.publishTransformed('userSubsToFires', function transform() {
}); });
}); });
Meteor.publish('subscriptions', function subscriptions() { Meteor.publish('mysubscriptions', function subscriptions() {
return Subscriptions.find({ owner: this.userId }); return Subscriptions.find({ owner: this.userId });
}); });

View file

@ -6,7 +6,10 @@ import { defaultCreatedAt, defaultUpdateAt } from '/imports/api/Utility/Utils.js
import i18n from 'i18next'; import i18n from 'i18next';
const schemaUserProfile = new SimpleSchema({ const schemaUserProfile = new SimpleSchema({
name: { type: String, optional: true }, // name: { type: String, optional: true },
name: Object,
'name.first': String,
'name.last': String,
lang: { type: String, optional: true }, lang: { type: String, optional: true },
telegramChatId: { type: Number, optional: true }, telegramChatId: { type: Number, optional: true },
telegramUsername: { type: String, optional: true }, telegramUsername: { type: String, optional: true },

View file

@ -11,7 +11,8 @@ export const defaultCreatedAt = {
} }
// Prevent user from supplying their own value // Prevent user from supplying their own value
this.unset(); this.unset();
} },
optional: true // TODO: Use migration for users
}; };
export const defaultUpdateAt = { export const defaultUpdateAt = {

View file

@ -1,11 +1,13 @@
/* global setTimeout */ /* global setTimeout */
/* eslint-disable react/jsx-indent-props */ /* eslint-disable react/jsx-indent-props */
/* eslint-disable import/no-absolute-path */ /* eslint-disable import/no-absolute-path */
/* eslint-disable import/no-absolute-path */
import React, { Component } from 'react'; import React, { Component } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Map, TileLayer, Marker, CircleMarker, Circle } from 'react-leaflet'; import { Map, TileLayer, Marker, CircleMarker, Circle } from 'react-leaflet';
import Leaflet from 'leaflet'; import Leaflet from 'leaflet';
import { translate } from 'react-i18next'; import { Trans, translate } from 'react-i18next';
import { withTracker } from 'meteor/react-meteor-data'; import { withTracker } from 'meteor/react-meteor-data';
import update from 'immutability-helper'; import update from 'immutability-helper';
import geolocation from '/imports/startup/client/geolocation'; import geolocation from '/imports/startup/client/geolocation';
@ -15,6 +17,8 @@ import 'leaflet-graphicscale/dist/Leaflet.GraphicScale.min.js';
import 'leaflet-sleep/Leaflet.Sleep.js'; import 'leaflet-sleep/Leaflet.Sleep.js';
import Control from 'react-leaflet-control'; import Control from 'react-leaflet-control';
import { Button, ButtonToolbar } from 'react-bootstrap'; import { Button, ButtonToolbar } from 'react-bootstrap';
import { Sidebar, Tab } from 'react-leaflet-sidebarv2';
import '/imports/ui/stylesheets/leaflet-sidebar.min.css';
import './SelectionMap.scss'; import './SelectionMap.scss';
class SelectionMap extends Component { class SelectionMap extends Component {
@ -25,7 +29,9 @@ class SelectionMap extends Component {
marker: props.center, marker: props.center,
zoom: 11, zoom: 11,
distance: props.distance, distance: props.distance,
draggable: true draggable: true,
sidebarCollapsed: true,
sidebarSelected: 'home'
}; };
this.getMap = this.getMap.bind(this); this.getMap = this.getMap.bind(this);
@ -79,6 +85,7 @@ class SelectionMap extends Component {
this.setState(update(this.state, { $merge: { marker: [lat, lng] } })); this.setState(update(this.state, { $merge: { marker: [lat, lng] } }));
this.props.onSelection({ lat, lng, currentDistance }); this.props.onSelection({ lat, lng, currentDistance });
this.fit(); this.fit();
// this.addScale();
} }
fit() { fit() {
@ -103,62 +110,120 @@ class SelectionMap extends Component {
return this.state.center && this.state.center[0] && this.state.distance; return this.state.center && this.state.center[0] && this.state.distance;
} }
onSidebarClose() {
this.setState({ sidebarCollapsed: true });
}
onSidebarOpen(id) {
this.setState({
sidebarCollapsed: false,
sidebarSelected: id
});
}
aTab(oid, name) {
return (
<Tab id={`sidetab-${oid}`} header={name} icon="fa fa-map-marker">
<div className="btn-group-vertical sidebar-tab-btn-group">
<Button
bsStyle="default"
><i className="fa fa-pencil-square-o" />
<Trans>Editar</Trans>
</Button>
<Button
bsStyle="default"
><i className="icons icon-target" />
<Trans>Centrar aquí</Trans>
</Button>
<Button
bsStyle="default"
>
<Trans>Desahabilitar</Trans>
</Button>
<Button
bsStyle="danger"
><i className="fa fa-times" />
<Trans>Borrar</Trans>
</Button>
</div>
</Tab>
);
}
render() { render() {
// console.log('render map called'); // console.log('render map called');
return ( return (
<div> <div>
{ this.isValidState() && { this.isValidState() &&
<Map <div className="leaflet-container">
center={this.state.center} <Sidebar
zoom={this.state.zoom} id="sidebar"
ref={(map) => { this.selectionMap = map; }} collapsed={this.state.sidebarCollapsed}
sleep={window.location.pathname === '/'} selected={this.state.sidebarSelected}
sleepTime={10750} closeIcon="fa fa-chevron-left"
wakeTime={750} onOpen={this.onSidebarOpen.bind(this)}
sleepNote onClose={this.onSidebarClose.bind(this)}
hoverToWake >
wakeMessage={this.props.t('Pulsa para activar')} <Tab id="home" header="Suscripciones" icon="fa fa-bars">
sleepOpacity={0.6} <p>Pulsa en alguna de tus suscripciones para editarlas, etc.</p>
> </Tab>
<TileLayer {this.aTab('someid2', 'Some fire')}
attribution="&amp;copy <a href=&quot;http://osm.org/copyright&quot;>OpenStreetMap</a> contributors" {this.aTab('someid3', 'Some another fire')}
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" </Sidebar>
/> <Map
<Marker className="sidebar-map"
draggable={this.state.draggable} center={this.state.center}
onDragend={this.updatePosition} zoom={this.state.zoom}
position={this.state.marker} ref={(map) => { this.selectionMap = map; }}
icon={positionIcon} sleep={window.location.pathname === '/'}
title={this.props.t('Arrastrar para seleccionar otro punto')} sleepTime={10750}
ref={(ref) => { this.marker = ref; }} wakeTime={750}
/> sleepNote
<CircleMarker hoverToWake
center={this.state.marker} wakeMessage={this.props.t('Pulsa para activar')}
color="red" sleepOpacity={0.6}
stroke={false} >
fillOpacity="1" <TileLayer
fill attribution="&amp;copy <a href=&quot;http://osm.org/copyright&quot;>OpenStreetMap</a> contributors"
radius={3} url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/> />
<Circle <Marker
center={this.state.marker} draggable={this.state.draggable}
ref={(ref) => { this.distanceCircle = ref; }} onDragend={this.updatePosition}
color="#145A32" position={this.state.marker}
fillColor="green" icon={positionIcon}
fillOpacity={0.1} title={this.props.t('Arrastrar para seleccionar otro punto')}
radius={this.state.distance * 1000} ref={(ref) => { this.marker = ref; }}
/> />
<Control position="topright" > <CircleMarker
<ButtonToolbar> center={this.state.marker}
<Button color="red"
bsStyle="success" stroke={false}
onClick={event => this.onSubs(event)} fillOpacity="1"
> fill
{this.props.subsBtn} radius={3}
</Button> />
</ButtonToolbar> <Circle
</Control> center={this.state.marker}
</Map> } ref={(ref) => { this.distanceCircle = ref; }}
color="#145A32"
fillColor="green"
fillOpacity={0.1}
radius={this.state.distance * 1000}
/>
<Control position="topright" >
<ButtonToolbar>
<Button
bsStyle="success"
onClick={event => this.onSubs(event)}
>
{this.props.subsBtn}
</Button>
</ButtonToolbar>
</Control>
</Map>
</div>
}
</div> </div>
); );
} }

View file

@ -7,6 +7,11 @@
margin: 30px auto; margin: 30px auto;
} }
div.leaflet-container {
position: relative;
margin: 0;
}
@media only screen and (max-width: 400px) { @media only screen and (max-width: 400px) {
.leaflet-container { .leaflet-container {
height: 400px; height: 400px;
@ -21,3 +26,23 @@
.center-in-my-pos { .center-in-my-pos {
margin: 10px; margin: 10px;
} }
.sidebar-tabs>li>a,
.sidebar-tabs>ul>li>a {
display: inline-block;
}
.sidebar {
width: 300px;
}
.sidebar-left~.sidebar-map .leaflet-left {
left: 315px;
}
.sidebar-tab-btn-group {
margin-top: 10px;
}
div.sidebar-tab-btn-group > button > i {
margin-right: 3px;
}

View file

@ -40,7 +40,7 @@ class SubscriptionEditor extends React.Component {
<FireSubscription <FireSubscription
center={[doc.location.lat, doc.location.lon]} center={[doc.location.lat, doc.location.lon]}
distance={doc.distance} distance={doc.distance}
focusInput={!isEdit} focusInput={this.props.focusInput ? this.props.focusInput : !isEdit}
subsBtn={isEdit ? t('Actualizar') : t('Subscribirme a fuegos en este rádio')} subsBtn={isEdit ? t('Actualizar') : t('Subscribirme a fuegos en este rádio')}
onSubs={state => this.onSubs(state)} onSubs={state => this.onSubs(state)}
/> />
@ -54,7 +54,8 @@ SubscriptionEditor.defaultProps = {
SubscriptionEditor.propTypes = { SubscriptionEditor.propTypes = {
doc: PropTypes.object, doc: PropTypes.object,
t: PropTypes.func.isRequired, t: PropTypes.func.isRequired,
history: PropTypes.object.isRequired history: PropTypes.object.isRequired,
focusInput: PropTypes.bool
}; };
export default translate([], { wait: true })(SubscriptionEditor); export default translate([], { wait: true })(SubscriptionEditor);

View file

@ -59,7 +59,7 @@ class Index extends Component {
const fullPageOptions = { const fullPageOptions = {
activeClass: 'active', // the class that is appended to the sections links activeClass: 'active', // the class that is appended to the sections links
anchors: ['home', 'crowdsourcing', 'platforms', 'participe', 'fires'], // the anchors for each sections anchors: ['home', 'crowdsourcing', 'platforms', 'participe', 'fires'], // the anchors for each sections
arrowNavigation: false, // use arrow keys (true after development) arrowNavigation: true, // use arrow keys (true after development)
className: 'section-container', // the class name for the section container className: 'section-container', // the class name for the section container
delay: 1000, // the scroll animation speed delay: 1000, // the scroll animation speed
navigation: true, // use dots navigation navigation: true, // use dots navigation
@ -219,7 +219,7 @@ 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} /> <SubscriptionEditor history={this.props.history} focusInput={false} />
</div> </div>
<div className="overlay" /> <div className="overlay" />
</Section> </Section>

View file

@ -96,10 +96,10 @@ Subscriptions.propTypes = {
export default translate([], { wait: true })(withTracker(() => { export default translate([], { wait: true })(withTracker(() => {
const subscription = Meteor.subscribe('subscriptions'); const subscription = Meteor.subscribe('mysubscriptions');
// console.log(UserSubsToFiresCollection.find().fetch()); // console.log(UserSubsToFiresCollection.find().fetch());
return { return {
loading: !subscription.ready(), loading: !subscription.ready(),
subscriptions: UserSubsToFiresCollection.find().fetch() subscriptions: UserSubsToFiresCollection.find({ owner: Meteor.userId() }).fetch()
}; };
})(Subscriptions)); })(Subscriptions));

View file

@ -0,0 +1 @@
.sidebar{position:absolute;top:0;bottom:0;width:100%;overflow:hidden;z-index:2000;box-shadow:0 1px 5px rgba(0,0,0,.65)}.sidebar.collapsed{width:40px}@media (min-width:768px) and (max-width:991px){.sidebar{width:305px}.sidebar-pane{min-width:265px}}@media (min-width:992px) and (max-width:1199px){.sidebar{width:390px}}@media (min-width:1200px){.sidebar{width:460px}}.sidebar-left{left:0}.sidebar-right{right:0}@media (min-width:768px){.sidebar{top:10px;bottom:10px;transition:width .5s}.sidebar-left{left:10px}.sidebar-right{right:10px}}.sidebar-tabs{top:0;bottom:0;height:100%;background-color:#fff}.sidebar-left .sidebar-tabs{left:0}.sidebar-right .sidebar-tabs{right:0}.sidebar-tabs,.sidebar-tabs>ul{position:absolute;width:40px;margin:0;padding:0}.sidebar-tabs>li,.sidebar-tabs>ul>li{width:100%;height:40px;color:#333;font-size:12pt;overflow:hidden;transition:all 80ms}.sidebar-tabs>li:hover,.sidebar-tabs>ul>li:hover{color:#000;background-color:#eee}.sidebar-tabs>li.active,.sidebar-tabs>ul>li.active{color:#fff;background-color:#0074d9}.sidebar-tabs>li.disabled,.sidebar-tabs>ul>li.disabled{color:rgba(51,51,51,.4)}.sidebar-tabs>li.disabled:hover,.sidebar-tabs>ul>li.disabled:hover{background:0 0}.sidebar-tabs>li.disabled>a,.sidebar-tabs>ul>li.disabled>a{cursor:default}.sidebar-tabs>li>a,.sidebar-tabs>ul>li>a{display:block;width:100%;height:100%;line-height:40px;color:inherit;text-decoration:none;text-align:center}.sidebar-tabs>ul+ul{bottom:0}.sidebar-content{position:absolute;top:0;bottom:0;background-color:rgba(255,255,255,.95);overflow-x:hidden;overflow-y:auto}.sidebar-left .sidebar-content{left:40px;right:0}.sidebar-right .sidebar-content{left:0;right:40px}.sidebar.collapsed>.sidebar-content{overflow-y:hidden}.sidebar-pane{display:none;left:0;right:0;box-sizing:border-box;padding:10px 20px}.sidebar-pane.active{display:block}.sidebar-header{margin:-10px -20px 0;height:40px;padding:0 20px;line-height:40px;font-size:14.4pt;color:#fff;background-color:#0074d9}.sidebar-right .sidebar-header{padding-left:40px}.sidebar-close{position:absolute;top:0;width:40px;height:40px;text-align:center;cursor:pointer}.sidebar-left .sidebar-close{right:0}.sidebar-right .sidebar-close{left:0}.sidebar-left~.sidebar-map{margin-left:40px}.sidebar-right~.sidebar-map{margin-right:40px}.sidebar.leaflet-touch{box-shadow:none;border-right:2px solid rgba(0,0,0,.2)}@media (min-width:768px) and (max-width:991px){.sidebar-left~.sidebar-map .leaflet-left{left:315px}.sidebar-right~.sidebar-map .leaflet-right{right:315px}}@media (min-width:992px) and (max-width:1199px){.sidebar-pane{min-width:350px}.sidebar-left~.sidebar-map .leaflet-left{left:400px}.sidebar-right~.sidebar-map .leaflet-right{right:400px}}@media (min-width:1200px){.sidebar-pane{min-width:420px}.sidebar-left~.sidebar-map .leaflet-left{left:470px}.sidebar-right~.sidebar-map .leaflet-right{right:470px}}@media (min-width:768px){.sidebar-left~.sidebar-map{margin-left:0}.sidebar-right~.sidebar-map{margin-right:0}.sidebar{border-radius:4px}.sidebar.leaflet-touch{border:2px solid rgba(0,0,0,.2)}.sidebar-left~.sidebar-map .leaflet-left{transition:left .5s}.sidebar-left.collapsed~.sidebar-map .leaflet-left{left:50px}.sidebar-right~.sidebar-map .leaflet-right{transition:right .5s}.sidebar-right.collapsed~.sidebar-map .leaflet-right{right:50px}}

5
package-lock.json generated
View file

@ -9955,6 +9955,11 @@
"resolved": "https://registry.npmjs.org/react-leaflet-google/-/react-leaflet-google-3.2.0.tgz", "resolved": "https://registry.npmjs.org/react-leaflet-google/-/react-leaflet-google-3.2.0.tgz",
"integrity": "sha1-67F5qBIOQ0Ui0GK8eTdojKLtD20=" "integrity": "sha1-67F5qBIOQ0Ui0GK8eTdojKLtD20="
}, },
"react-leaflet-sidebarv2": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/react-leaflet-sidebarv2/-/react-leaflet-sidebarv2-0.5.1.tgz",
"integrity": "sha512-HaEeIq3TfK1Hy7xIuX3hv895LNpu1xIBCgW+I1INrqX/95nNRw6ItNQOxmk0YO5ImrSaDeV6rDXKsAGojjngdg=="
},
"react-overlays": { "react-overlays": {
"version": "0.7.4", "version": "0.7.4",
"resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-0.7.4.tgz", "resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-0.7.4.tgz",

View file

@ -53,6 +53,7 @@
"react-leaflet": "^1.7.7", "react-leaflet": "^1.7.7",
"react-leaflet-control": "^1.4.0", "react-leaflet-control": "^1.4.0",
"react-leaflet-google": "^3.2.0", "react-leaflet-google": "^3.2.0",
"react-leaflet-sidebarv2": "^0.5.1",
"react-places-autocomplete": "^5.4.3", "react-places-autocomplete": "^5.4.3",
"react-resize-detector": "^1.1.0", "react-resize-detector": "^1.1.0",
"react-router-bootstrap": "^0.24.4", "react-router-bootstrap": "^0.24.4",