diff --git a/package-lock.json b/package-lock.json index ac8d216..1c97132 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3051,6 +3051,16 @@ "sha.js": "^2.4.8" } }, + "create-react-class": { + "version": "15.6.3", + "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.3.tgz", + "integrity": "sha512-M+/3Q6E6DLO6Yx3OwrWjwHBnvfXXYA7W+dFjt/ZDBemHO1DDZhsalX/NUtnTYclN6GfnBDRh4qRHjcDHmlJBJg==", + "requires": { + "fbjs": "^0.8.9", + "loose-envify": "^1.3.1", + "object-assign": "^4.1.1" + } + }, "cross-spawn": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", @@ -3567,6 +3577,40 @@ } } }, + "disqus-react": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/disqus-react/-/disqus-react-1.0.5.tgz", + "integrity": "sha1-go6hhr0kHF4qbf66tRNeUqLZHRE=", + "requires": { + "react": "^15.6.1", + "react-dom": "^15.6.1" + }, + "dependencies": { + "react": { + "version": "15.6.2", + "resolved": "https://registry.npmjs.org/react/-/react-15.6.2.tgz", + "integrity": "sha1-26BDSrQ5z+gvEI8PURZjkIF5qnI=", + "requires": { + "create-react-class": "^15.6.0", + "fbjs": "^0.8.9", + "loose-envify": "^1.1.0", + "object-assign": "^4.1.0", + "prop-types": "^15.5.10" + } + }, + "react-dom": { + "version": "15.6.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-15.6.2.tgz", + "integrity": "sha1-Qc+t9pO3V/rycIRDodH9WgK+9zA=", + "requires": { + "fbjs": "^0.8.9", + "loose-envify": "^1.1.0", + "object-assign": "^4.1.0", + "prop-types": "^15.5.10" + } + } + } + }, "dns-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", diff --git a/package.json b/package.json index e45497e..51de5d0 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "dependencies": { "@material-ui/core": "^3.1.0", "@material-ui/icons": "^3.0.1", + "disqus-react": "^1.0.5", "firebase": "^5.5.2", "firebase-admin": "^6.0.0", "flamelink": "^0.19.2", diff --git a/src/App.css b/src/App.css index 9494272..b5b1e22 100644 --- a/src/App.css +++ b/src/App.css @@ -13,4 +13,38 @@ body { .report-google-map-container > div { width: calc(100% - 50% - 120px) !important; } +} + +#disqus_thread { + width: 89% !important; + margin: 0 auto; +} + +.sighting-list { + height: calc(50vh - 64px); + overflow-y: scroll; +} + +@media (min-width: 960px) { + .sighting-list { + height: calc(100vh - 64px); + overflow-y: scroll; + } +} + +.sighting-details-content { + width: 89%; + margin: 330px auto 0 auto; +} + +.sighting-detail-google-map-container > div { + width: 100% !important; + height: 300px !important; +} + +@media (min-width: 960px) { + .sighting-detail-google-map-container > div { + width: calc(100% - 50% - 120px) !important; + height: 300px !important; + } } \ No newline at end of file diff --git a/src/components/Flamelink.js b/src/components/Flamelink.js index fd3187d..3958057 100644 --- a/src/components/Flamelink.js +++ b/src/components/Flamelink.js @@ -2,7 +2,6 @@ import { Component } from 'react'; class Flamelink extends Component { render() { - return(null); } } diff --git a/src/components/Main.js b/src/components/Main.js index 62336af..0332dbe 100644 --- a/src/components/Main.js +++ b/src/components/Main.js @@ -68,7 +68,7 @@ const styles = theme => ({ class ResponsiveDrawer extends React.Component { state = { mobileOpen: false, - key: '', + key: 'Home', open: false }; @@ -83,7 +83,7 @@ class ResponsiveDrawer extends React.Component { nav = (text) => { this.setState({ key: text - }) + }); } render() { @@ -149,7 +149,7 @@ class ResponsiveDrawer extends React.Component { - The American Marten + Marten Tracker @@ -189,9 +189,9 @@ class ResponsiveDrawer extends React.Component { {this.state.key === 'Report' && } {this.state.key === 'Map' && } {this.state.key === 'List' && } - {this.state.key === 'Easy-Quiz' && } - {this.state.key === 'Medium-Quiz' && } - {this.state.key === 'Hard-Quiz' && } + {this.state.key === 'Easy-Quiz' && } + {this.state.key === 'Medium-Quiz' && } + {this.state.key === 'Hard-Quiz' && } ); diff --git a/src/components/QuizGame.js b/src/components/QuizGame.js index 1339978..88f8bbe 100644 --- a/src/components/QuizGame.js +++ b/src/components/QuizGame.js @@ -239,33 +239,33 @@ class QuizGame extends React.Component { * @param {*} difficulty The difficulty setting passed in. */ pickDifficulty = difficulty => { - let level + let level; switch (difficulty) { case 'Easy': - this.easy.questions = this.shuffleArray(this.easy.questions) - level = this.easy - break + this.easy.questions = this.shuffleArray(this.easy.questions); + level = this.easy; + break; case 'Medium': - this.medium.questions = this.shuffleArray(this.medium.questions) - level = this.medium - break + this.medium.questions = this.shuffleArray(this.medium.questions); + level = this.medium; + break; case 'Hard': - this.hard.questions = this.shuffleArray(this.hard.questions) - level = this.hard - break + this.hard.questions = this.shuffleArray(this.hard.questions); + level = this.hard; + break; default: - break + break; } - return level + return level; } // The state of the component. state = { difficulty: this.pickDifficulty(this.props.difficulty), key: Math.random() - } + }; // Renders the quiz component. render() { diff --git a/src/components/ReportForm.js b/src/components/ReportForm.js index 7d50473..9057b89 100644 --- a/src/components/ReportForm.js +++ b/src/components/ReportForm.js @@ -11,6 +11,8 @@ import CloseIcon from '@material-ui/icons/Close'; import Button from '@material-ui/core/Button'; import firebase from '../firebase.js'; import GoogleMap from '../components/ReportMap'; +import Modal from '@material-ui/core/Modal'; +import Typography from '@material-ui/core/Typography'; /** * Styles that the different @@ -18,84 +20,78 @@ import GoogleMap from '../components/ReportMap'; * in. Mostly used for spacing. */ const styles = theme => ({ - container: { - display: 'flex', - flexWrap: 'wrap' - }, - textField: { - marginLeft: theme.spacing.unit * 2, - marginRight: theme.spacing.unit, - marginTop: theme.spacing.unit * 2, - flexBasis: 280, - width: '90%' - }, - button: { - marginLeft: theme.spacing.unit * 2, - marginRight: theme.spacing.unit, - marginTop: theme.spacing.unit * 2, - }, - dense: { - marginTop: 30, - }, - close: { - padding: theme.spacing.unit / 2, - }, - icon: { - fontSize: 20, - marginRight: theme.spacing.unit, - }, - message: { - display: 'flex', - alignItems: 'center', - }, - menu: { - width: 200, - }, + container: { + display: 'flex', + flexWrap: 'wrap' + }, + textField: { + marginLeft: theme.spacing.unit * 2, + marginRight: theme.spacing.unit, + marginTop: theme.spacing.unit * 2, + flexBasis: 280, + width: '90%' + }, + button: { + marginLeft: theme.spacing.unit * 2, + marginRight: theme.spacing.unit, + marginTop: theme.spacing.unit * 2, + }, + dense: { + marginTop: 30, + }, + close: { + padding: theme.spacing.unit / 2, + }, + icon: { + fontSize: 20, + marginRight: theme.spacing.unit, + }, + message: { + display: 'flex', + alignItems: 'center', + }, + menu: { + width: 200, + }, + paper: { + position: 'absolute', + width: theme.spacing.unit * 50, + backgroundColor: theme.palette.background.paper, + boxShadow: theme.shadows[5], + padding: theme.spacing.unit * 4, + } }); -/** - * Function for formatting the - * year as a string that - * Material UI can use. - * @param {*} date, Date passed in. - */ -function getYear(date) { - var d = new Date(date), - year = d.getFullYear(); - - return year; -} - /** * Types of sightings. Label is what is * viewed in the application, value is * what is stored in the database. */ const sightingTypes = [ - { - value: 'visual', - label: 'Visual', - }, - { - value: 'roadkill', - label: 'Roadkill', - }, - { - value: 'trapped', - label: 'Trapped', - }, - { - value: 'viewed_tracks', - label: 'Viewed Tracks', - }, - { - value: 'photo', - label: 'Photo', - }, - { - value: 'other', - label: 'Other', - }, + { + value: 'visual', + label: 'Visual', + }, + { + value: 'roadkill', + label: 'Roadkill', + }, + { + value: 'trapped', + label: 'Trapped', + }, + { + value: 'viewed_tracks', + label: 'Viewed Tracks', + }, + { + value: 'photo', + label: 'Photo', + }, + { + value: 'other', + label: 'Other', + }, ]; /** @@ -104,54 +100,54 @@ const sightingTypes = [ * what is stored in the database. */ const monthTypes = [ - { - value: '01', - label: 'January', - }, - { - value: '02', - label: 'February', - }, - { - value: '03', - label: 'March', - }, - { - value: '04', - label: 'April', - }, - { - value: '05', - label: 'May', - }, - { - value: '06', - label: 'June', - }, - { - value: '07', - label: 'July', - }, - { - value: '08', - label: 'August', - }, - { - value: '09', - label: 'September', - }, - { - value: '10', - label: 'October', - }, - { - value: '11', - label: 'November', - }, - { - value: '12', - label: 'December', - }, + { + value: '01', + label: 'January', + }, + { + value: '02', + label: 'February', + }, + { + value: '03', + label: 'March', + }, + { + value: '04', + label: 'April', + }, + { + value: '05', + label: 'May', + }, + { + value: '06', + label: 'June', + }, + { + value: '07', + label: 'July', + }, + { + value: '08', + label: 'August', + }, + { + value: '09', + label: 'September', + }, + { + value: '10', + label: 'October', + }, + { + value: '11', + label: 'November', + }, + { + value: '12', + label: 'December', + }, ]; /** @@ -160,26 +156,26 @@ const monthTypes = [ * what is stored in the database. */ const timeTypes = [ - { - value: 'unknown', - label: 'Unknown', - }, - { - value: 'morning', - label: 'Morning', - }, - { - value: 'midday', - label: 'Midday', - }, - { - value: 'evening', - label: 'Evening', - }, - { - value: 'night', - label: 'Night', - }, + { + value: 'unknown', + label: 'Unknown', + }, + { + value: 'morning', + label: 'Morning', + }, + { + value: 'midday', + label: 'Midday', + }, + { + value: 'evening', + label: 'Evening', + }, + { + value: 'night', + label: 'Night', + }, ]; /** @@ -188,317 +184,379 @@ const timeTypes = [ * what is stored in the database. */ const confidenceLevels = [ - { - value: '1', - label: '1 - Strongly disagree', - }, - { - value: '2', - label: '2 - Disagree', - }, - { - value: '3', - label: '3 - Neutral', - }, - { - value: '4', - label: '4 - Agree', - }, - { - value: '5', - label: '5 - Strongly agree', - }, + { + value: '1', + label: '1 - Strongly disagree', + }, + { + value: '2', + label: '2 - Disagree', + }, + { + value: '3', + label: '3 - Neutral', + }, + { + value: '4', + label: '4 - Agree', + }, + { + value: '5', + label: '5 - Strongly agree', + }, ]; /** * The form component. */ class ReportForm extends React.Component { - /** - * Component contructor. Currently - * only used to bind event - * handlers. - */ - constructor() { - super(); - this.handleSubmit = this.handleSubmit.bind(this); - } - - /** - * State of form components. - */ - state = { - month: '01', - year: getYear(new Date()), - time: 'unknown', - type: 'visual', - confidence: '1', - desc: '', - lat: '', - lng: '', - open: false - }; - - /** - * Handles state change in form - * components. - */ - handleChange = name => event => { - this.setState({ - [name]: event.target.value, - }); - }; - - /** - * Handles closing the toast. - */ - handleClose = (event, reason) => { - if (reason === 'clickaway') { - return; + /** + * Component contructor. Currently + * only used to bind event + * handlers. + */ + constructor() { + super(); + this.handleSubmit = this.handleSubmit.bind(this); } - this.setState({ open: false }); - }; - - /* - * Get the coordinates - * - */ - getCoordinates = (lat, lng) => { - let latitude = lat; - let longitude = lng; - - this.setState({ - lat: latitude, - lng: longitude - }); - } - - /** - * Event listener for form. - * When the form is submitted, - * this function passes the - * data along to Firebase. - */ - handleSubmit(e) { - e.preventDefault(); - const sightingsRef = firebase.database().ref('sightings'); - const sighting = { - type: this.state.type, - confidence: this.state.confidence, - date: this.state.year + '-' + this.state.month, - time: this.state.time, - desc: this.state.desc, - lat: this.state.lat, - lng: this.state.lng - } - sightingsRef.push(sighting); - this.setState({ - year: getYear(new Date()), - month: '01', - time: 'unknown', - type: 'visual', - confidence: '1', - desc: '', - lat: '', - lng: '', - open: true - }); - }; - - - /** - * The render method for this component. - */ - render() { - const { classes } = this.props; - /** - * The actual form. + * Function for formatting the + * year as a string that + * Material UI can use. + * @param {*} date, Date passed in. + */ + getYear = date => { + var d = new Date(date), + year = d.getFullYear(); + + return year; + } + + /** + * Function for formatting the + * month as a string that + * Material UI can use. + * @param {*} date, Date passed in. + */ + getMonth = date => { + var d = new Date(date), + month = d.getMonth() + 1; + + month = month.toString(); + + if (month.length === 1) { + month = "0" + month; + } + + return month; + } + + /** + * State of form components. */ - return ( - -
- - - - - - {sightingTypes.map(option => ( - - {option.label} - - ))} - - + state = { + month: this.getMonth(new Date()), + year: this.getYear(new Date()), + time: 'unknown', + type: 'visual', + confidence: '1', + desc: '', + lat: '', + lng: '', + open: false, + openModal: false, + hasModalOpened: false + }; - - - {confidenceLevels.map(option => ( - - {option.label} - - ))} - - + handleModalOpen = () => !this.state.hasModalOpened ? this.setState({ openModal: true, hasModalOpened: true }) : null; - - - {timeTypes.map(option => ( - - {option.label} - - ))} - - + handleModalClose = () => { + this.setState({ openModal: false }); + }; - - - {monthTypes.map(option => ( - - {option.label} - - ))} - - + getModalStyle = () => { + return { + top: `25%`, + left: `75%`, + transform: `translate(-25%, -75%)`, + }; + } - - - + /** + * Handles state change in form + * components. + */ + handleChange = name => event => { + this.setState({ + [name]: event.target.value, + }); + }; - - - + /** + * Handles closing the toast. + */ + handleClose = (event, reason) => { + if (reason === 'clickaway') { + return; + } - - - - - - - - - -
- Report received.} - action={[ - - - , - ]} - /> -
- ); - } + this.setState({ open: false }); + }; + + /* + * Get the coordinates + * + */ + getCoordinates = (lat, lng) => { + let latitude = lat; + let longitude = lng; + + this.setState({ + lat: latitude, + lng: longitude + }); + } + + /** + * Event listener for form. + * When the form is submitted, + * this function passes the + * data along to Firebase. + */ + handleSubmit(e) { + e.preventDefault(); + const sightingsRef = firebase.database().ref('sightings'); + const sighting = { + type: this.state.type, + confidence: this.state.confidence, + date: this.state.year + '-' + this.state.month, + time: this.state.time, + desc: this.state.desc, + lat: this.state.lat, + lng: this.state.lng + } + sightingsRef.push(sighting); + this.setState({ + year: this.getYear(new Date()), + month: this.getMonth(new Date()), + time: 'unknown', + type: 'visual', + confidence: '1', + desc: '', + lat: '', + lng: '', + open: true + }); + }; + + + /** + * The render method for this component. + */ + render() { + const { classes } = this.props; + + /** + * The actual form. + */ + return ( + +
+ + + + + + {sightingTypes.map(option => ( + + {option.label} + + ))} + + + + + + {confidenceLevels.map(option => ( + + {option.label} + + ))} + + + + + + {timeTypes.map(option => ( + + {option.label} + + ))} + + + + + + {monthTypes.map(option => ( + + {option.label} + + ))} + + + + + + + + + + + + + + + + + + +
+ + Need a little help? + + + Click on the map to drop a pin! + +
+
+
+
+
+ Report received.} + action={[ + + + , + ]} + /> +
+ ); + } } ReportForm.propTypes = { - classes: PropTypes.object.isRequired, + classes: PropTypes.object.isRequired, }; export default withStyles(styles)(ReportForm); \ No newline at end of file diff --git a/src/components/ReportMap.js b/src/components/ReportMap.js index 882a49f..f4848ee 100644 --- a/src/components/ReportMap.js +++ b/src/components/ReportMap.js @@ -9,7 +9,7 @@ const API_KEY = 'AIzaSyAZ_0J01bA6wCbIPK4UBq2RUBC-hIqG4mM'; const mapStyles = { width: '100%', height: '100%' -} +}; export class MapContainer extends Component { @@ -18,22 +18,22 @@ export class MapContainer extends Component { if (navigator.geolocation) { navigator.geolocation.getCurrentPosition((position) => { this.setState({ - myLatLng: { - lat: position.coords.latitude, - lng: position.coords.longitude - } + myLatLng: { + lat: position.coords.latitude, + lng: position.coords.longitude } + } ); }) } else { // If browser doesn't support geolocation or if user does not allow it, // center map on Grand Rapids, Michigan this.setState({ - myLatLng: { - lat: 42.9634, - lng: 85.6681 - } + myLatLng: { + lat: 42.9634, + lng: 85.6681 } + } ); } } @@ -52,7 +52,7 @@ export class MapContainer extends Component { showingInfoWindow: true }); } - + // When the user clicks on the map, if a info window is visible then close it // and 'unactive' that marker onMapClick = (props, map, e) => { @@ -68,13 +68,13 @@ export class MapContainer extends Component { lat: e.latLng.lat(), lng: e.latLng.lng() } - }) + }); let lat = e.latLng.lat(); - let lng = e.latLng.lng(); + let lng = e.latLng.lng(); if (this.props.onClick) { - this.props.onClick(lat,lng); + this.props.onClick(lat, lng); } } @@ -98,36 +98,36 @@ export class MapContainer extends Component { return ( // Render the Google Map, Marker, and InfoWindow components -
+
+ style={mapStyles} + google={this.props.google} + initialCenter={this.state.myLatLng} + center={this.state.myLatLng} + defaultZoom={15} + onClick={this.onMapClick} > - - + marker={this.state.activeMarker} + visible={this.state.showingInfoWindow} > - - { this.state.selectedPlace.title } + + {this.state.selectedPlace.title} - - { this.state.selectedPlace.name } + + {this.state.selectedPlace.name} @@ -138,6 +138,4 @@ export class MapContainer extends Component { } // Send the Google Map API Key with the MapContainer component -export default GoogleApiWrapper({ - apiKey: (API_KEY) -})(MapContainer) \ No newline at end of file +export default GoogleApiWrapper({ apiKey: (API_KEY) })(MapContainer); \ No newline at end of file diff --git a/src/components/SightingDetail.js b/src/components/SightingDetail.js new file mode 100644 index 0000000..01618b8 --- /dev/null +++ b/src/components/SightingDetail.js @@ -0,0 +1,160 @@ +import React, { Component, Fragment } from 'react'; +import Disqus from 'disqus-react'; +import moment from 'moment'; +import SightingDetailMap from './SightingDetailMap'; + +/** + * Types of sightings. Label is what is + * viewed in the application, value is + * what is stored in the database. + */ + const sightingTypes = [ + { + value: 'visual', + label: 'Visual', + }, + { + value: 'roadkill', + label: 'Roadkill', + }, + { + value: 'trapped', + label: 'Trapped', + }, + { + value: 'viewed_tracks', + label: 'Viewed Tracks', + }, + { + value: 'photo', + label: 'Photo', + }, + { + value: 'other', + label: 'Other', + }, +]; + +/** + * Types of sightings. Label is what is + * viewed in the application, value is + * what is stored in the database. +*/ +const timeTypes = [ + { + value: 'unknown', + label: 'Unknown', + }, + { + value: 'morning', + label: 'Morning', + }, + { + value: 'midday', + label: 'Midday', + }, + { + value: 'evening', + label: 'Evening', + }, + { + value: 'night', + label: 'Night', + }, +]; + +/** + * Levels of confidence. Label is what is + * viewed in the application, value is + * what is stored in the database. +*/ +const confidenceLevels = [ + { + value: '1', + label: '1 - Strongly disagree', + }, + { + value: '2', + label: '2 - Disagree', + }, + { + value: '3', + label: '3 - Neutral', + }, + { + value: '4', + label: '4 - Agree', + }, + { + value: '5', + label: '5 - Strongly agree', + }, +]; + +class SightingDetail extends Component { + + /** + * Gets formatted type value. + */ + getType = item => { + for (var i = 0; i < sightingTypes.length; i++) { + if (sightingTypes[i].value === item) { + return sightingTypes[i].label; + } + } + } + + + /** + * Gets formatted time value. + */ + getTime = item => { + for (var i = 0; i < timeTypes.length; i++) { + if (timeTypes[i].value === item) { + return timeTypes[i].label; + } + } + } + + /** + * Gets formatted confidence value. + */ + getConfidence = item => { + for (var i = 0; i < confidenceLevels.length; i++) { + if (confidenceLevels[i].value === item) { + return confidenceLevels[i].label; + } + } + + } + + formatDate = date => { + return (moment(date, "YYYY-MM").format("MMMM YYYY").toString()); + } + + render() { + const disqusShortname = 'https-marten-application-netlify-com'; + const disqusConfig = { + url: `http://localhost:3000/${this.props.detail.id}`, + identifier: this.props.detail.id, + title: this.props.detail.id + }; + + return ( + + +
+

Type: {this.getType(this.props.detail.type)}

+

When: {this.formatDate(this.props.detail.date)}, {this.getTime(this.props.detail.time)}

+

Where: {this.props.detail.lat} degrees N, and {this.props.detail.lng} degrees E

+

I am confident of my sighting: {this.getConfidence(this.props.detail.confidence)}

+
+

{`${this.props.detail.desc}`}

+
+ +
+ ); + } +} + +export default SightingDetail; \ No newline at end of file diff --git a/src/components/SightingDetailMap.js b/src/components/SightingDetailMap.js new file mode 100644 index 0000000..775e7ee --- /dev/null +++ b/src/components/SightingDetailMap.js @@ -0,0 +1,36 @@ +import React, { Component } from 'react'; +import { Map, Marker, GoogleApiWrapper } from 'google-maps-react'; + +// Google Maps API Key +const API_KEY = 'AIzaSyAZ_0J01bA6wCbIPK4UBq2RUBC-hIqG4mM'; + +// Map container styles +const mapStyles = { + width: '100%', + height: '100%' +}; + +export class MapContainer extends Component { + + render() { + return ( + // Render the Google Map, Marker, and InfoWindow components +
+ + + + +
+ ); + } +} + +// Send the Google Map API Key with the MapContainer component +export default GoogleApiWrapper({ apiKey: (API_KEY) })(MapContainer); \ No newline at end of file diff --git a/src/components/SightingMap.js b/src/components/SightingMap.js index 72790de..8b71c07 100644 --- a/src/components/SightingMap.js +++ b/src/components/SightingMap.js @@ -1,6 +1,6 @@ import React, { Component, Fragment } from 'react'; import { Map, InfoWindow, Marker, GoogleApiWrapper } from 'google-maps-react'; -import moment from 'moment' +import moment from 'moment'; import Typography from '@material-ui/core/Typography'; import firebase from '../firebase.js'; @@ -11,7 +11,7 @@ const API_KEY = 'AIzaSyAZ_0J01bA6wCbIPK4UBq2RUBC-hIqG4mM'; const mapStyles = { width: '100%', height: '100%' -} +}; /** * Types of sightings. Label is what is @@ -81,60 +81,26 @@ const timeTypes = [ const confidenceLevels = [ { value: '1', - label: '1 - Strongly unconfident', + label: '1 - Strongly disagree', }, { value: '2', - label: '2 - Unconfident', + label: '2 - Disagree', }, { value: '3', - label: '3 - Somewhat confident', + label: '3 - Neutral', }, { value: '4', - label: '4 - Confident', + label: '4 - Agree', }, { value: '5', - label: '5 - Very confident', + label: '5 - Strongly agree', }, ]; -/** - * Gets formatted confidence value. - */ -function getConfidence(item) { - for (var i = 0; i < confidenceLevels.length; i++) { - if (confidenceLevels[i].value === item) { - return confidenceLevels[i].label; - } - } - -} - -/** - * Gets formatted time value. - */ -function getTime(item) { - for (var i = 0; i < timeTypes.length; i++) { - if (timeTypes[i].value === item) { - return timeTypes[i].label; - } - } -} - -/** - * Gets formatted type value. - */ -function getType(item) { - for (var i = 0; i < sightingTypes.length; i++) { - if (sightingTypes[i].value === item) { - return sightingTypes[i].label; - } - } -} - export class MapContainer extends Component { // Get the user's location using Google's geolocation @@ -146,9 +112,8 @@ export class MapContainer extends Component { lat: position.coords.latitude, lng: position.coords.longitude } - } - ); - }) + }); + }); } else { // If browser doesn't support geolocation or if user does not allow it, // center map on Grand Rapids, Michigan @@ -157,11 +122,45 @@ export class MapContainer extends Component { lat: 42.9634, lng: 85.6681 } - } - ); + }); } } + /** + * Gets formatted type value. + */ + getType = item => { + for (var i = 0; i < sightingTypes.length; i++) { + if (sightingTypes[i].value === item) { + return sightingTypes[i].label; + } + } + } + + + /** + * Gets formatted time value. + */ + getTime = item => { + for (var i = 0; i < timeTypes.length; i++) { + if (timeTypes[i].value === item) { + return timeTypes[i].label; + } + } + } + + /** + * Gets formatted confidence value. + */ + getConfidence = item => { + for (var i = 0; i < confidenceLevels.length; i++) { + if (confidenceLevels[i].value === item) { + return confidenceLevels[i].label; + } + } + + } + // When the component has mounted to the DOM, get the user's location componentDidMount() { this.getLocation(); @@ -231,7 +230,6 @@ export class MapContainer extends Component { default: break } - return pinIcon } @@ -250,7 +248,7 @@ export class MapContainer extends Component { activeMarker: {}, selectedPlace: {}, sightings: [] - } + }; render() { const {google} = this.props; @@ -264,27 +262,29 @@ export class MapContainer extends Component { initialCenter={this.state.myLatLng} center={this.state.myLatLng} defaultZoom={15} - onClick={this.onMapClick} > + onClick={this.onMapClick} + > - - {this.state.sightings.map((sighting) => { - let pinIcon = this.sightingIcon(sighting.type) + /> + {this.state.sightings.map((sighting) => { + + let pinIcon = this.sightingIcon(sighting.type) + return ( Confidence: {getConfidence(sighting.confidence)}} - date = {Date: {this.formatDate(sighting.date)}} - time = {Time: {getTime(sighting.time)}} - description = {Description: {sighting.desc}} + key={sighting.id} + position={{ lat: sighting.lat, lng: sighting.lng }} + onClick={this.onMarkerClick} + type={'Type: ' + this.getType(sighting.type)} + date={Date: {this.formatDate(sighting.date)}} + time={Time: {this.getTime(sighting.time)}} + confidence={I am confident of my sighting: {this.getConfidence(sighting.confidence)}} + description={Description: {sighting.desc}} icon={{ url: pinIcon, anchor: new google.maps.Point(32,32), @@ -302,15 +302,15 @@ export class MapContainer extends Component { {this.state.selectedPlace.type} - - {this.state.selectedPlace.confidence} - {this.state.selectedPlace.date} {this.state.selectedPlace.time} + + {this.state.selectedPlace.confidence} + {this.state.selectedPlace.description} @@ -323,6 +323,4 @@ export class MapContainer extends Component { } // Send the Google Map API Key with the MapContainer component -export default GoogleApiWrapper({ - apiKey: (API_KEY) -})(MapContainer) \ No newline at end of file +export default GoogleApiWrapper({ apiKey: (API_KEY) })(MapContainer); \ No newline at end of file diff --git a/src/components/ViewSightings.js b/src/components/ViewSightings.js index 822cf8e..ec5d3c2 100644 --- a/src/components/ViewSightings.js +++ b/src/components/ViewSightings.js @@ -1,232 +1,95 @@ -import React, {Fragment} from 'react'; -import PropTypes from 'prop-types'; +import React, { Component, Fragment } from 'react'; import Grid from '@material-ui/core/Grid'; -import TextField from '@material-ui/core/TextField' -import Button from '@material-ui/core/Button' -import Paper from '@material-ui/core/Paper' -import { withStyles } from '@material-ui/core/styles'; -import Typography from '@material-ui/core/Typography'; import firebase from '../firebase.js'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemText from '@material-ui/core/ListItemText'; +import SightingDetail from './SightingDetail'; -const styles = theme => ({ - root: { - ...theme.mixins.gutters(), - paddingTop: theme.spacing.unit * 2, - paddingBottom: theme.spacing.unit * 2, - }, - container: { - display: 'flex', - flexWrap: 'wrap', - }, - textField: { - marginLeft: theme.spacing.unit * 2, - marginRight: theme.spacing.unit * 2, - marginTop: theme.spacing.unit * 2, - flexBasis: 280, - }, - button: { - marginLeft: theme.spacing.unit * 3, - marginRight: theme.spacing.unit * 3, - marginTop: theme.spacing.unit * 3, - }, - paper: { - marginLeft: theme.spacing.unit * 2, - marginRight: theme.spacing.unit, - marginTop: theme.spacing.unit * 2, - }, - }); +class ViewSightings extends Component { - /** - * Types of sightings. Label is what is - * viewed in the application, value is - * what is stored in the database. - */ - const sightingTypes = [ - { - value: 'visual', - label: 'Visual', - }, - { - value: 'roadkill', - label: 'Roadkill', - }, - { - value: 'trapped', - label: 'Trapped', - }, - { - value: 'viewed_tracks', - label: 'Viewed Tracks', - }, - { - value: 'photo', - label: 'Photo', - }, - { - value: 'other', - label: 'Other', - }, - ]; - - /** - * Levels of confidence. Label is what is - * viewed in the application, value is - * what is stored in the database. - */ - const confidenceLevels = [ - { - value: '1', - label: '1 - Strongly unconfident', - }, - { - value: '2', - label: '2 - Unconfident', - }, - { - value: '3', - label: '3 - Somewhat confident', - }, - { - value: '4', - label: '4 - Confident', - }, - { - value: '5', - label: '5 - Very confident', - }, - ]; + componentDidMount() { + const sightingsRef = firebase.database().ref('sightings'); - /** - * Gets formatted confidence value. - */ - function getConfidence(item) { - for (var i = 0; i < confidenceLevels.length; i++) { - if (confidenceLevels[i].value === item) { - return confidenceLevels[i].label; + sightingsRef.on('value', (snapshot) => { + let sightings = snapshot.val(); + let newState = []; + + for (let sighting in sightings) { + newState.unshift({ + id: sighting, + lat: sightings[sighting].lat, + lng: sightings[sighting].lng, + desc: sightings[sighting].desc, + type: sightings[sighting].type, + confidence: sightings[sighting].confidence, + date: sightings[sighting].date, + time: sightings[sighting].time + }); } - } - + this.setState({ + sightings: newState + }); + }); } - /** - * Gets formatted type value. - */ - function getType(item) { - for (var i = 0; i < sightingTypes.length; i++) { - if (sightingTypes[i].value === item) { - return sightingTypes[i].label; - } - } - } - -class ViewSightings extends React.Component { - constructor(props){ - super(props); - - this.handleSubmit = this.handleSubmit.bind(this); + getDetail = (id, lat, lng, desc, type, confidence, date, time) => { + this.setState({ + selectedSighting: { + id: id, + lat: lat, + lng: lng, + desc: desc, + type: type, + confidence: confidence, + date: date, + time: time + }, + clicked: true + }) } state = { - id: '', - type: 'N/A', - confidence: 'N/A', - date: 'N/A', - time: 'N/A', - desc: 'N/A', - lat: 'N/A', - lng: 'N/A' - }; - - - /** - * Handles state change. - */ - handleChange = name => event => { - this.setState({ - [name]: event.target.value, - }); + sightings: [], + selectedSighting: { + id: null, + lat: null, + lng: null, + desc: null, + type: null, + confidence: null, + date: null, + time: null + }, + clicked: false }; - /** - * Handles submit on search. - */ - handleSubmit(e){ - e.preventDefault(); - const itemSighting = firebase.database().ref("sightings/" + this.state.id); - itemSighting.once("value").then((snapshot) => { - // Die if there's no data for that ID. - if (!snapshot.exists()) { - return; - } - - let data = snapshot.val(); - - this.setState({ - date: data.date, - time: data.time, - type: getType(data.type), - confidence: getConfidence(data.confidence), - desc: data.desc, - lat: data.lat, - lng: data.lng - }); - }); - }; - - render(){ - const { classes } = this.props; + render() { return ( - /** - * The below houses the search - * and submit button along with - * the sighting information - * it pulls. - */ - -
- - + + + + + { + this.state.sightings.map((sighting) => { + return ( + this.getDetail(sighting.id, sighting.lat, sighting.lng, sighting.desc, sighting.type, sighting.confidence, sighting.date, sighting.time)}> + + + ); + }) + } + + - - - - - - - - Sighting - - - Type: {this.state.type} {
} - Confidence: {this.state.confidence} {
} - Date: {this.state.date} {
} - Time: {this.state.time} {
} - Latitude: {this.state.lat} {
} - Longitude: {this.state.lng} {
} - Description: {this.state.desc} -
-
+ + {this.state.clicked === true && }
- ) - + ); } - } -ViewSightings.propTypes = { - classes: PropTypes.object.isRequired, - }; - -export default withStyles(styles)(ViewSightings); \ No newline at end of file +export default ViewSightings; \ No newline at end of file diff --git a/src/pages/SightingList.js b/src/pages/SightingList.js index 9d035b9..0540527 100644 --- a/src/pages/SightingList.js +++ b/src/pages/SightingList.js @@ -1,13 +1,10 @@ import React, { Component } from 'react'; import ViewSightings from '../components/ViewSightings.js'; -import Typography from '@material-ui/core/Typography'; class Sighting extends Component { render() { return ( - - - + ); } }