From 4d0188d144e9cee804bc190061a2529967f95eb2 Mon Sep 17 00:00:00 2001 From: Al Duncanson Date: Thu, 25 Oct 2018 21:26:18 -0400 Subject: [PATCH 01/15] added sighting list, and custom comment thread --- package-lock.json | 44 +++++ package.json | 1 + src/components/SightingDetail.js | 27 +++ src/components/ViewSightings.js | 287 ++++++++----------------------- src/pages/SightingList.js | 5 +- 5 files changed, 149 insertions(+), 215 deletions(-) create mode 100644 src/components/SightingDetail.js diff --git a/package-lock.json b/package-lock.json index 02471bb..67f79c2 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 a8ae1ad..45dfc0b 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/components/SightingDetail.js b/src/components/SightingDetail.js new file mode 100644 index 0000000..d1deb03 --- /dev/null +++ b/src/components/SightingDetail.js @@ -0,0 +1,27 @@ +import React, { Component, Fragment } from 'react'; +import Disqus from 'disqus-react'; + +class SightingDetail extends Component { + + componentDidUpdate() { + window.history.pushState("", "", this.props.detail.id); + } + + 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 ( + +

{this.props.detail.type}

+ +
+ ); + } +} + +export default SightingDetail; diff --git a/src/components/ViewSightings.js b/src/components/ViewSightings.js index 822cf8e..57a81f3 100644 --- a/src/components/ViewSightings.js +++ b/src/components/ViewSightings.js @@ -1,232 +1,97 @@ -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 ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemText from '@material-ui/core/ListItemText'; +import HomeIcon from '@material-ui/icons/Home'; +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.push({ + 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' - }; - + sightings: [], + selectedSighting: { + id: null, + lat: null, + lng: null, + desc: null, + type: null, + confidence: null, + date: null, + time: null + }, + clicked: false + } - /** - * Handles state change. - */ - handleChange = name => event => { - this.setState({ - [name]: event.target.value, - }); - }; - - /** - * 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; 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 ( - - - + ); } } From 15e853640d114338de90b424f2ef3c1fb28c05b5 Mon Sep 17 00:00:00 2001 From: Al Duncanson Date: Thu, 25 Oct 2018 21:55:48 -0400 Subject: [PATCH 02/15] removed window state --- src/components/SightingDetail.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/components/SightingDetail.js b/src/components/SightingDetail.js index d1deb03..f0e47ef 100644 --- a/src/components/SightingDetail.js +++ b/src/components/SightingDetail.js @@ -3,10 +3,6 @@ import Disqus from 'disqus-react'; class SightingDetail extends Component { - componentDidUpdate() { - window.history.pushState("", "", this.props.detail.id); - } - render() { const disqusShortname = 'https-marten-application-netlify-com'; const disqusConfig = { From 4f064e546eb06e16d216d16f3f4111b41b7f38e4 Mon Sep 17 00:00:00 2001 From: Al Duncanson Date: Fri, 26 Oct 2018 11:04:44 -0400 Subject: [PATCH 03/15] added styles and sighting details --- src/App.css | 4 ++++ src/components/SightingDetail.js | 10 +++++++++- src/components/ViewSightings.js | 15 ++++++++------- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/App.css b/src/App.css index 9494272..439e3fd 100644 --- a/src/App.css +++ b/src/App.css @@ -13,4 +13,8 @@ body { .report-google-map-container > div { width: calc(100% - 50% - 120px) !important; } +} + +#disqus_thread { + width: 99% !important; } \ No newline at end of file diff --git a/src/components/SightingDetail.js b/src/components/SightingDetail.js index f0e47ef..47dee0b 100644 --- a/src/components/SightingDetail.js +++ b/src/components/SightingDetail.js @@ -1,5 +1,6 @@ import React, { Component, Fragment } from 'react'; import Disqus from 'disqus-react'; +import Divider from '@material-ui/core/Divider'; class SightingDetail extends Component { @@ -13,7 +14,14 @@ class SightingDetail extends Component { return ( -

{this.props.detail.type}

+

{`Sighting ${this.props.detail.id}`}

+ +

{`Type: ${this.props.detail.type}`}

+

{`Confidence: ${this.props.detail.confidence}`}

+

{`When: ${this.props.detail.date}, ${this.props.detail.time}`}

+

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

+

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

+
); diff --git a/src/components/ViewSightings.js b/src/components/ViewSightings.js index 57a81f3..0da8e16 100644 --- a/src/components/ViewSightings.js +++ b/src/components/ViewSightings.js @@ -75,13 +75,14 @@ class ViewSightings extends Component { { this.state.sightings.map((sighting) => { - return ( - this.getDetail(sighting.id, sighting.lat, sighting.lng, sighting.desc, sighting.type, sighting.confidence, sighting.date, sighting.time)}> - - - - ) - })} + return ( + this.getDetail(sighting.id, sighting.lat, sighting.lng, sighting.desc, sighting.type, sighting.confidence, sighting.date, sighting.time)}> + + + + ) + }) + } From 66fb1fc6f13cc4c1dae8f2e34fefb0e83afd9f02 Mon Sep 17 00:00:00 2001 From: Al Duncanson Date: Fri, 26 Oct 2018 13:24:10 -0400 Subject: [PATCH 04/15] quick changes --- src/components/Main.js | 2 +- src/components/ViewSightings.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Main.js b/src/components/Main.js index 62336af..be9617f 100644 --- a/src/components/Main.js +++ b/src/components/Main.js @@ -149,7 +149,7 @@ class ResponsiveDrawer extends React.Component { - The American Marten + Marten Tracker diff --git a/src/components/ViewSightings.js b/src/components/ViewSightings.js index 0da8e16..1b46368 100644 --- a/src/components/ViewSightings.js +++ b/src/components/ViewSightings.js @@ -78,7 +78,7 @@ class ViewSightings extends Component { return ( this.getDetail(sighting.id, sighting.lat, sighting.lng, sighting.desc, sighting.type, sighting.confidence, sighting.date, sighting.time)}> - + ) }) From 49eaa2a4f453994a8479bfbeb3be89a3fa6cadec Mon Sep 17 00:00:00 2001 From: Al Duncanson Date: Fri, 26 Oct 2018 17:43:09 -0400 Subject: [PATCH 05/15] added sighting detail map --- src/components/SightingDetail.js | 2 ++ src/components/SightingDetailMap.js | 38 +++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 src/components/SightingDetailMap.js diff --git a/src/components/SightingDetail.js b/src/components/SightingDetail.js index 47dee0b..97dc9ac 100644 --- a/src/components/SightingDetail.js +++ b/src/components/SightingDetail.js @@ -1,6 +1,7 @@ import React, { Component, Fragment } from 'react'; import Disqus from 'disqus-react'; import Divider from '@material-ui/core/Divider'; +import SightingDetailMap from './SightingDetailMap'; class SightingDetail extends Component { @@ -21,6 +22,7 @@ class SightingDetail extends Component {

{`When: ${this.props.detail.date}, ${this.props.detail.time}`}

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

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

+ diff --git a/src/components/SightingDetailMap.js b/src/components/SightingDetailMap.js new file mode 100644 index 0000000..aaa4c50 --- /dev/null +++ b/src/components/SightingDetailMap.js @@ -0,0 +1,38 @@ +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 From 75438399851c716783c6455cb7ec61ad88b3bf3e Mon Sep 17 00:00:00 2001 From: Al Duncanson Date: Sat, 27 Oct 2018 16:08:53 -0400 Subject: [PATCH 06/15] best I got right now --- src/App.css | 9 +++++++++ src/components/SightingDetail.js | 15 ++++++--------- src/components/ViewSightings.js | 2 +- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/App.css b/src/App.css index 439e3fd..0612f07 100644 --- a/src/App.css +++ b/src/App.css @@ -17,4 +17,13 @@ body { #disqus_thread { width: 99% !important; +} + +.sighting-details { + margin-top: 330px; +} + +.sighting-detail-google-map-container > div { + width: calc(100% - 50% - 120px) !important; + height: 50% !important; } \ No newline at end of file diff --git a/src/components/SightingDetail.js b/src/components/SightingDetail.js index 97dc9ac..ffcaa27 100644 --- a/src/components/SightingDetail.js +++ b/src/components/SightingDetail.js @@ -1,6 +1,5 @@ import React, { Component, Fragment } from 'react'; import Disqus from 'disqus-react'; -import Divider from '@material-ui/core/Divider'; import SightingDetailMap from './SightingDetailMap'; class SightingDetail extends Component { @@ -15,15 +14,13 @@ class SightingDetail extends Component { return ( -

{`Sighting ${this.props.detail.id}`}

- -

{`Type: ${this.props.detail.type}`}

-

{`Confidence: ${this.props.detail.confidence}`}

-

{`When: ${this.props.detail.date}, ${this.props.detail.time}`}

-

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

-

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

- +
+

{`Confidence: ${this.props.detail.confidence}`}

+

{`When: ${this.props.detail.date}, ${this.props.detail.time}`}

+

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

+

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

+
); diff --git a/src/components/ViewSightings.js b/src/components/ViewSightings.js index 1b46368..9abd481 100644 --- a/src/components/ViewSightings.js +++ b/src/components/ViewSightings.js @@ -95,4 +95,4 @@ class ViewSightings extends Component { } } -export default ViewSightings; +export default ViewSightings; \ No newline at end of file From 7736f3ff33fed65be3c6bdbcbac013e1544c69f9 Mon Sep 17 00:00:00 2001 From: Al Duncanson Date: Sat, 27 Oct 2018 16:59:36 -0400 Subject: [PATCH 07/15] made list scrollable --- src/App.css | 7 ++++++- src/components/SightingDetail.js | 2 +- src/components/ViewSightings.js | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/App.css b/src/App.css index 0612f07..4e8368c 100644 --- a/src/App.css +++ b/src/App.css @@ -19,7 +19,12 @@ body { width: 99% !important; } -.sighting-details { +.sighting-list { + height: calc(100vh - 64px); + overflow-y: scroll; +} + +.sighting-details-content { margin-top: 330px; } diff --git a/src/components/SightingDetail.js b/src/components/SightingDetail.js index ffcaa27..73df886 100644 --- a/src/components/SightingDetail.js +++ b/src/components/SightingDetail.js @@ -15,7 +15,7 @@ class SightingDetail extends Component { return ( -
+

{`Confidence: ${this.props.detail.confidence}`}

{`When: ${this.props.detail.date}, ${this.props.detail.time}`}

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

diff --git a/src/components/ViewSightings.js b/src/components/ViewSightings.js index 9abd481..fe59599 100644 --- a/src/components/ViewSightings.js +++ b/src/components/ViewSightings.js @@ -70,7 +70,7 @@ class ViewSightings extends Component { return ( - + { @@ -86,7 +86,7 @@ class ViewSightings extends Component { - + {this.state.clicked === true && } From 5366b67a9828a164f8d522f7e885b994eb117457 Mon Sep 17 00:00:00 2001 From: Al Duncanson Date: Sat, 27 Oct 2018 17:14:16 -0400 Subject: [PATCH 08/15] fixed map sizing --- src/App.css | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/App.css b/src/App.css index 4e8368c..25421d2 100644 --- a/src/App.css +++ b/src/App.css @@ -29,6 +29,13 @@ body { } .sighting-detail-google-map-container > div { - width: calc(100% - 50% - 120px) !important; - height: 50% !important; + width: 100% !important; + height: 30% !important; +} + +@media (min-width: 960px) { + .sighting-detail-google-map-container > div { + width: calc(100% - 50% - 120px) !important; + height: 50% !important; + } } \ No newline at end of file From 71b6ce9ad7581a907f4aa3aa0f603b7c93cd1a4f Mon Sep 17 00:00:00 2001 From: Al Duncanson Date: Sat, 27 Oct 2018 17:24:42 -0400 Subject: [PATCH 09/15] fixed sighting detail and disqus size --- src/App.css | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/App.css b/src/App.css index 25421d2..b5b1e22 100644 --- a/src/App.css +++ b/src/App.css @@ -16,26 +16,35 @@ body { } #disqus_thread { - width: 99% !important; + width: 89% !important; + margin: 0 auto; } .sighting-list { - height: calc(100vh - 64px); + height: calc(50vh - 64px); overflow-y: scroll; } +@media (min-width: 960px) { + .sighting-list { + height: calc(100vh - 64px); + overflow-y: scroll; + } +} + .sighting-details-content { - margin-top: 330px; + width: 89%; + margin: 330px auto 0 auto; } .sighting-detail-google-map-container > div { width: 100% !important; - height: 30% !important; + height: 300px !important; } @media (min-width: 960px) { .sighting-detail-google-map-container > div { width: calc(100% - 50% - 120px) !important; - height: 50% !important; + height: 300px !important; } } \ No newline at end of file From 94f5222c6143f4a5de7457940f906cc2f55d1f70 Mon Sep 17 00:00:00 2001 From: Al Duncanson Date: Sat, 27 Oct 2018 18:07:30 -0400 Subject: [PATCH 10/15] merging --- package-lock.json | 5 + package.json | 1 + public/index.html | 32 +++-- src/components/QuizGame.js | 89 ++++++++++---- src/components/ReportForm.js | 56 ++++++++- src/components/SightingMap.js | 207 ++++++++++++++++++++++++++------ src/components/ViewSightings.js | 3 - 7 files changed, 312 insertions(+), 81 deletions(-) diff --git a/package-lock.json b/package-lock.json index 67f79c2..1c97132 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9592,6 +9592,11 @@ "integrity": "sha512-9DITV2YEMcw7XojdfvGl3gDD8J9QjZTJ7ZOUuSAkP+F3T6rDbzMJuPktxptsdHYEvZcmXrCD3LMOhdSAEq6zKA==", "optional": true }, + "moment": { + "version": "2.22.2", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz", + "integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y=" + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", diff --git a/package.json b/package.json index 45dfc0b..51de5d0 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "firebase-admin": "^6.0.0", "flamelink": "^0.19.2", "google-maps-react": "^2.0.2", + "moment": "^2.22.2", "react": "^16.5.1", "react-dom": "^16.5.1", "react-quiz-component": "0.2.0", diff --git a/public/index.html b/public/index.html index ebb2c8a..d949bd5 100644 --- a/public/index.html +++ b/public/index.html @@ -1,6 +1,17 @@ + + + + + @@ -30,15 +41,16 @@ You need to enable JavaScript to run this app.
- + + diff --git a/src/components/QuizGame.js b/src/components/QuizGame.js index bbfe640..1339978 100644 --- a/src/components/QuizGame.js +++ b/src/components/QuizGame.js @@ -3,21 +3,9 @@ import Grid from '@material-ui/core/Grid'; import PropTypes from 'prop-types'; import { withStyles } from '@material-ui/core/styles'; import Quiz from 'react-quiz-component'; - -/** - * Shuffles a given array. - * @param {*} array The array passed in. - */ -function shuffleArray(array) { - var j, x, i; - for (i = array.length - 1; i > 0; i--) { - j = Math.floor(Math.random() * (i + 1)); - x = array[i]; - array[i] = array[j]; - array[j] = x; - } - return array; -} +import { Typography } from '@material-ui/core'; +import Button from '@material-ui/core/Button'; +import RefreshIcon from '@material-ui/icons/Refresh'; // Style for the tabs. const styles = theme => ({ @@ -25,13 +13,45 @@ const styles = theme => ({ flexGrow: 1, backgroundColor: theme.palette.background.paper, }, + button: { + margin: theme.spacing.unit, + }, + rightIcon: { + marginLeft: theme.spacing.unit, + }, }); class QuizGame extends React.Component { + /** + * Shuffles a given array. + * @param {*} array The array passed in. + */ + shuffleArray = array => { + let shuffled = array; + + var j, x, i; + + for (i = shuffled.length - 1; i > 0; i--) { + j = Math.floor(Math.random() * (i + 1)); + x = shuffled[i]; + shuffled[i] = shuffled[j]; + shuffled[j] = x; + } + + return shuffled; + } + + reset = () => { + this.setState({ + difficulty: this.pickDifficulty(this.props.difficulty), + key: Math.random() + }); + } + easy = { "quizTitle": "Trail Cam Quiz: Easy", - "questions": shuffleArray([ + "questions": [ { "question": What animal is this?

, "questionType": "text", @@ -87,12 +107,12 @@ class QuizGame extends React.Component { ], "correctAnswer": "3" }, - ]) + ] } medium = { "quizTitle": "Trail Cam Quiz: Medium", - "questions": shuffleArray([ + "questions": [ { "question": What animal is this?

, "questionType": "text", @@ -148,12 +168,12 @@ class QuizGame extends React.Component { ], "correctAnswer": "3" }, - ]) + ] } hard = { "quizTitle": "Trail Cam Quiz: Hard", - "questions": shuffleArray([ + "questions": [ { "question": What animal is this?

, "questionType": "text", @@ -209,20 +229,29 @@ class QuizGame extends React.Component { ], "correctAnswer": "3" }, - ]) + ] } + /** + * This function returns the + * quiz data based on the difficulty + * level passed into it. + * @param {*} difficulty The difficulty setting passed in. + */ pickDifficulty = difficulty => { let level switch (difficulty) { case 'Easy': + 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 case 'Hard': + this.hard.questions = this.shuffleArray(this.hard.questions) level = this.hard break default: @@ -234,8 +263,8 @@ class QuizGame extends React.Component { // The state of the component. state = { - //difficulty: pickDifficulty(this.props.difficulty) - difficulty: this.pickDifficulty(this.props.difficulty) + difficulty: this.pickDifficulty(this.props.difficulty), + key: Math.random() } // Renders the quiz component. @@ -245,9 +274,17 @@ class QuizGame extends React.Component { return ( // Tabs
- - - + + + + + + + +
); } diff --git a/src/components/ReportForm.js b/src/components/ReportForm.js index 9815843..7d50473 100644 --- a/src/components/ReportForm.js +++ b/src/components/ReportForm.js @@ -4,6 +4,10 @@ import Grid from '@material-ui/core/Grid'; import { withStyles } from '@material-ui/core/styles'; import MenuItem from '@material-ui/core/MenuItem'; import TextField from '@material-ui/core/TextField'; +import CheckCircleIcon from '@material-ui/icons/CheckCircle'; +import Snackbar from '@material-ui/core/Snackbar'; +import IconButton from '@material-ui/core/IconButton'; +import CloseIcon from '@material-ui/icons/Close'; import Button from '@material-ui/core/Button'; import firebase from '../firebase.js'; import GoogleMap from '../components/ReportMap'; @@ -33,6 +37,17 @@ const styles = theme => ({ dense: { marginTop: 30, }, + close: { + padding: theme.spacing.unit / 2, + }, + icon: { + fontSize: 20, + marginRight: theme.spacing.unit, + }, + message: { + display: 'flex', + alignItems: 'center', + }, menu: { width: 200, }, @@ -220,7 +235,8 @@ class ReportForm extends React.Component { confidence: '1', desc: '', lat: '', - lng: '' + lng: '', + open: false }; /** @@ -233,6 +249,17 @@ class ReportForm extends React.Component { }); }; + /** + * Handles closing the toast. + */ + handleClose = (event, reason) => { + if (reason === 'clickaway') { + return; + } + + this.setState({ open: false }); + }; + /* * Get the coordinates * @@ -274,7 +301,8 @@ class ReportForm extends React.Component { confidence: '1', desc: '', lat: '', - lng: '' + lng: '', + open: true }); }; @@ -440,6 +468,30 @@ class ReportForm extends React.Component {
+ Report received.} + action={[ + + + , + ]} + />
); } diff --git a/src/components/SightingMap.js b/src/components/SightingMap.js index e2c3e89..5535b68 100644 --- a/src/components/SightingMap.js +++ b/src/components/SightingMap.js @@ -1,5 +1,6 @@ import React, { Component, Fragment } from 'react'; import { Map, InfoWindow, Marker, GoogleApiWrapper } from 'google-maps-react'; +import moment from 'moment' import Typography from '@material-ui/core/Typography'; import firebase from '../firebase.js'; @@ -12,6 +13,128 @@ const mapStyles = { height: '100%' } +/** + * 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 unconfident', + }, + { + value: '2', + label: '2 - Unconfident', + }, + { + value: '3', + label: '3 - Somewhat confident', + }, + { + value: '4', + label: '4 - Confident', + }, + { + value: '5', + label: '5 - Very confident', + }, +]; + +/** + * 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 @@ -19,22 +142,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 } + } ); } } @@ -85,6 +208,10 @@ export class MapContainer extends Component { } } + formatDate = date => { + return (moment(date, "YYYY-MM").format("MMMM YYYY").toString()) + } + // Set the state of the component to contain user coordinates and initial // marker and info window information state = { @@ -101,55 +228,55 @@ export class MapContainer extends Component { render() { 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} > - - { this.state.sightings.map((sighting) => { + {this.state.sightings.map((sighting) => { return ( Confidence: {getConfidence(sighting.confidence)}} + date = {Date: {this.formatDate(sighting.date)}} + time = {Time: {getTime(sighting.time)}} + description = {Description: {sighting.desc}} /> ) })} + marker={this.state.activeMarker} + visible={this.state.showingInfoWindow} > - - { this.state.selectedPlace.type } + + {this.state.selectedPlace.type} - - { this.state.selectedPlace.confidence } + + {this.state.selectedPlace.confidence} - - { this.state.selectedPlace.date } + + {this.state.selectedPlace.date} - - { this.state.selectedPlace.time } + + {this.state.selectedPlace.time} - - { this.state.selectedPlace.description } + + {this.state.selectedPlace.description} diff --git a/src/components/ViewSightings.js b/src/components/ViewSightings.js index fe59599..e245e6b 100644 --- a/src/components/ViewSightings.js +++ b/src/components/ViewSightings.js @@ -3,9 +3,7 @@ import Grid from '@material-ui/core/Grid'; import firebase from '../firebase.js'; import List from '@material-ui/core/List'; import ListItem from '@material-ui/core/ListItem'; -import ListItemIcon from '@material-ui/core/ListItemIcon'; import ListItemText from '@material-ui/core/ListItemText'; -import HomeIcon from '@material-ui/icons/Home'; import SightingDetail from './SightingDetail'; class ViewSightings extends Component { @@ -77,7 +75,6 @@ class ViewSightings extends Component { this.state.sightings.map((sighting) => { return ( this.getDetail(sighting.id, sighting.lat, sighting.lng, sighting.desc, sighting.type, sighting.confidence, sighting.date, sighting.time)}> - ) From c9f22b74d521715d2bce99f72e709e88b586c086 Mon Sep 17 00:00:00 2001 From: wildscotsmen Date: Mon, 29 Oct 2018 00:33:35 -0400 Subject: [PATCH 11/15] Cleaned code for consistency. Additionally added month function for report form. --- src/components/ReportForm.js | 55 +++++++++++++++-------- src/components/SightingMap.js | 85 ++++++++++++++++++----------------- 2 files changed, 80 insertions(+), 60 deletions(-) diff --git a/src/components/ReportForm.js b/src/components/ReportForm.js index 7d50473..b30952d 100644 --- a/src/components/ReportForm.js +++ b/src/components/ReportForm.js @@ -53,19 +53,6 @@ const styles = theme => ({ }, }); -/** - * 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 @@ -228,8 +215,8 @@ class ReportForm extends React.Component { * State of form components. */ state = { - month: '01', - year: getYear(new Date()), + month: this.getMonth(new Date()), + year: this.getYear(new Date()), time: 'unknown', type: 'visual', confidence: '1', @@ -249,6 +236,38 @@ class ReportForm extends React.Component { }); }; + /** + * 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(); + + month = month.toString(); + + if (month.length == 1) { + month = "0" + month; + } + + return month; + } + /** * Handles closing the toast. */ @@ -294,8 +313,8 @@ class ReportForm extends React.Component { } sightingsRef.push(sighting); this.setState({ - year: getYear(new Date()), - month: '01', + year: this.getYear(new Date()), + month: this.getMonth(new Date()), time: 'unknown', type: 'visual', confidence: '1', @@ -479,7 +498,7 @@ class ReportForm extends React.Component { ContentProps={{ 'aria-describedby': 'message-id', }} - message={Report received.} + message={Report received.} action={[ { + 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(); @@ -246,14 +247,14 @@ export class MapContainer extends Component { {this.state.sightings.map((sighting) => { 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)} + confidence={Confidence: {this.getConfidence(sighting.confidence)}} + date={Date: {this.formatDate(sighting.date)}} + time={Time: {this.getTime(sighting.time)}} + description={Description: {sighting.desc}} /> ) })} From 4617f31be21b9e5e56f3c76fc33b07180785ffee Mon Sep 17 00:00:00 2001 From: wildscotsmen Date: Mon, 29 Oct 2018 00:38:19 -0400 Subject: [PATCH 12/15] Fixed some issues with ordering in report form code. --- src/components/ReportForm.js | 64 ++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/src/components/ReportForm.js b/src/components/ReportForm.js index b30952d..9068eec 100644 --- a/src/components/ReportForm.js +++ b/src/components/ReportForm.js @@ -211,6 +211,38 @@ class ReportForm extends React.Component { this.handleSubmit = this.handleSubmit.bind(this); } + /** + * 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. */ @@ -236,38 +268,6 @@ class ReportForm extends React.Component { }); }; - /** - * 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(); - - month = month.toString(); - - if (month.length == 1) { - month = "0" + month; - } - - return month; - } - /** * Handles closing the toast. */ From bfbb27842ad8f23e6f490621ac84a5f31c38805f Mon Sep 17 00:00:00 2001 From: WildScotsmen Date: Wed, 31 Oct 2018 18:52:37 -0400 Subject: [PATCH 13/15] Cleaned up code according to what we decided on. Also formatted raw database data in SightingDetail component. Finally, ordered list in reverse order. --- src/components/Flamelink.js | 1 - src/components/Main.js | 8 +- src/components/QuizGame.js | 26 ++--- src/components/ReportForm.js | 6 +- src/components/ReportMap.js | 70 +++++++------- src/components/SightingDetail.js | 142 ++++++++++++++++++++++++++-- src/components/SightingDetailMap.js | 22 ++--- src/components/SightingMap.js | 41 ++++---- src/components/ViewSightings.js | 6 +- 9 files changed, 222 insertions(+), 100 deletions(-) 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 be9617f..cfe31d7 100644 --- a/src/components/Main.js +++ b/src/components/Main.js @@ -83,7 +83,7 @@ class ResponsiveDrawer extends React.Component { nav = (text) => { this.setState({ key: text - }) + }); } render() { @@ -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 9068eec..d55625f 100644 --- a/src/components/ReportForm.js +++ b/src/components/ReportForm.js @@ -266,7 +266,7 @@ getMonth = date => { this.setState({ [name]: event.target.value, }); - }; + } /** * Handles closing the toast. @@ -277,7 +277,7 @@ getMonth = date => { } this.setState({ open: false }); - }; + } /* * Get the coordinates @@ -323,7 +323,7 @@ getMonth = date => { lng: '', open: true }); - }; + } /** 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 index 73df886..01618b8 100644 --- a/src/components/SightingDetail.js +++ b/src/components/SightingDetail.js @@ -1,24 +1,154 @@ 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 ( - +
-

{`Confidence: ${this.props.detail.confidence}`}

-

{`When: ${this.props.detail.date}, ${this.props.detail.time}`}

-

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

+

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}`}

@@ -27,4 +157,4 @@ class SightingDetail extends Component { } } -export default SightingDetail; +export default SightingDetail; \ No newline at end of file diff --git a/src/components/SightingDetailMap.js b/src/components/SightingDetailMap.js index aaa4c50..775e7ee 100644 --- a/src/components/SightingDetailMap.js +++ b/src/components/SightingDetailMap.js @@ -8,23 +8,23 @@ const API_KEY = 'AIzaSyAZ_0J01bA6wCbIPK4UBq2RUBC-hIqG4mM'; const mapStyles = { width: '100%', height: '100%' -} +}; export class MapContainer extends Component { render() { return ( // Render the Google Map, Marker, and InfoWindow components -
+
+ style={mapStyles} + google={this.props.google} + initialCenter={{ lat: this.props.lat, lng: this.props.lng }} + center={{ lat: this.props.lat, lng: this.props.lng }} + defaultZoom={15}> -
@@ -33,6 +33,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/SightingMap.js b/src/components/SightingMap.js index 2d00465..85b7410 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,23 +81,23 @@ 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', }, ]; @@ -112,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 @@ -123,8 +122,7 @@ export class MapContainer extends Component { lat: 42.9634, lng: 85.6681 } - } - ); + }); } } @@ -210,7 +208,7 @@ export class MapContainer extends Component { } formatDate = date => { - return (moment(date, "YYYY-MM").format("MMMM YYYY").toString()) + return (moment(date, "YYYY-MM").format("MMMM YYYY").toString()); } // Set the state of the component to contain user coordinates and initial @@ -224,7 +222,7 @@ export class MapContainer extends Component { activeMarker: {}, selectedPlace: {}, sightings: [] - } + }; render() { return ( @@ -236,7 +234,8 @@ export class MapContainer extends Component { initialCenter={this.state.myLatLng} center={this.state.myLatLng} defaultZoom={15} - onClick={this.onMapClick} > + onClick={this.onMapClick} + > Confidence: {this.getConfidence(sighting.confidence)}} 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}} /> ) @@ -267,15 +266,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} @@ -288,6 +287,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 e245e6b..ec5d3c2 100644 --- a/src/components/ViewSightings.js +++ b/src/components/ViewSightings.js @@ -16,7 +16,7 @@ class ViewSightings extends Component { let newState = []; for (let sighting in sightings) { - newState.push({ + newState.unshift({ id: sighting, lat: sightings[sighting].lat, lng: sightings[sighting].lng, @@ -62,7 +62,7 @@ class ViewSightings extends Component { time: null }, clicked: false - } + }; render() { return ( @@ -77,7 +77,7 @@ class ViewSightings extends Component { this.getDetail(sighting.id, sighting.lat, sighting.lng, sighting.desc, sighting.type, sighting.confidence, sighting.date, sighting.time)}> - ) + ); }) } From 3f36ee55a19c22eb5ca49328d0e1e1c95c88eb36 Mon Sep 17 00:00:00 2001 From: WildScotsmen Date: Wed, 31 Oct 2018 19:14:24 -0400 Subject: [PATCH 14/15] Set default key of the application to Home. It was set to blank, which meant that the application wasn't on any page when the user first visits the application. --- src/components/Main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Main.js b/src/components/Main.js index be9617f..d93ef62 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 }; From 4301a3d6cd338f51f26e46da636591551ff23f0a Mon Sep 17 00:00:00 2001 From: Al Duncanson Date: Wed, 31 Oct 2018 22:18:45 -0400 Subject: [PATCH 15/15] added report map tooltip --- src/components/ReportForm.js | 937 ++++++++++++++++++----------------- 1 file changed, 488 insertions(+), 449 deletions(-) diff --git a/src/components/ReportForm.js b/src/components/ReportForm.js index 9068eec..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,39 +20,46 @@ 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, + } }); /** @@ -59,30 +68,30 @@ const styles = theme => ({ * 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', + }, ]; /** @@ -91,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', + }, ]; /** @@ -147,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', + }, ]; /** @@ -175,349 +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); - } - - /** - * 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. - */ - state = { - month: this.getMonth(new Date()), - year: this.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: 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. + * 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