aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mobile/package.json4
-rw-r--r--mobile/screens/Form1.js200
-rw-r--r--web/models/mockpoints.js10
-rw-r--r--web/models/points.js10
-rw-r--r--web/public/javascripts/map.js98
-rw-r--r--web/public/stylesheets/style.css32
-rw-r--r--web/routes/index.js11
-rw-r--r--web/views/map.pug21
8 files changed, 241 insertions, 145 deletions
diff --git a/mobile/package.json b/mobile/package.json
index 488e885..80a591e 100644
--- a/mobile/package.json
+++ b/mobile/package.json
@@ -15,8 +15,8 @@
"expo": "^32.0.0",
"react": "16.5.0",
"react-native": "https://github.com/expo/react-native/archive/sdk-32.0.0.tar.gz",
- "react-native-elements": "^1.1.0",
- "react-navigation": "^3.0.9"
+ "react-navigation": "^3.0.9",
+ "tcomb-form-native": "^0.6.20"
},
"devDependencies": {
"babel-preset-expo": "^5.0.0",
diff --git a/mobile/screens/Form1.js b/mobile/screens/Form1.js
index 2a52499..a14d647 100644
--- a/mobile/screens/Form1.js
+++ b/mobile/screens/Form1.js
@@ -1,9 +1,81 @@
import React, { Component } from 'react';
-import {Text, Alert, AppRegistry, StyleSheet, View } from 'react-native';
-import { Button, Input, CheckBox } from 'react-native-elements';
+import { StyleSheet, Button, View, KeyboardAvoidingView } from 'react-native';
import { Constants } from 'expo';
+import t from 'tcomb-form-native';
+
+const Form = t.form.Form;
+
+var Gender = t.enums({
+ "Male" : "Male",
+ "Female": "Female",
+ "Unsure": "Can't say for sure",
+});
+
+var AgeRanges = t.enums({
+ "0-20" : "Under 20 years",
+ "20-30": "20 - 30 years",
+ "30-50": "30 - 50 years",
+ "50+" : "Over 50 years",
+});
+
+var Races = t.enums({
+ "White" : "European or White",
+ "eAsian" : "East Asian",
+ "sAsian" : "South Asian",
+ "Black" : "Black or African American",
+ "Aboriginal": "Aboriginal",
+ "Other" : "Other",
+});
+
+const Report = t.struct({
+ gender : Gender,
+ age : AgeRanges,
+ race : Races,
+ longhair : t.Boolean,
+ longbeard : t.Boolean,
+ extra : t.maybe(t.String)
+});
+
+const options = {
+ label: "Help us identify them!",
+ fields: {
+ gender: {
+ auto: 'none',
+ nullOption: {value: '', text: "Gender"},
+ error: "Please pick an option for this field",
+ },
+ age: {
+ auto: 'none',
+ nullOption: {value: '', text: "Guess their age"},
+ error: "Please pick an option for this field"
+ },
+ race: {
+ auto: 'none',
+ nullOption: {value: '', text: "Race"},
+ error: "Please pick an option for this field",
+ },
+ longhair: {
+ label: "Long hair?"
+ },
+ longbeard: {
+ label: "Long beard?"
+ },
+ extra: {
+ label: "Distinctive features",
+ help: "How would you identify them in a crowd? Tattoos, peircings, holding a sign etc.",
+ },
+ },
+};
export default class Form1 extends Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ value: null,
+ };
+ }
+
getApiUrl() {
var releaseChannel = Constants.manifest.releaseChannel;
if (releaseChannel === undefined) return Constants.manifest.extra.apiUrl.dev
@@ -11,83 +83,81 @@ export default class Form1 extends Component {
if (releaseChannel.indexOf('staging') !== -1) return Constants.manifest.extra.apiUrl.staging
}
- _onPressButton1() {
+ _onSubmitPress(values) {
const { navigation } = this.props;
const coordinates = navigation.getParam('coordinates');
- var uri = this.getApiUrl();
- fetch(uri, {
+ var url = this.getApiUrl();
+ fetch(url, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
- 'coordinates': coordinates,
- 'type': "Point",
- 'isInjured': this.state.injured,
- 'reasonForHelp': this.state.why,
- 'ageRange': this.state.age,
- 'clothingDescription': this.state.appearance
- }),
- });
- // TODO: handle the response from the serve and decide what to display
- // based on that
- // for now just to back to the home page
- this.props.navigation.navigate('GreetingPage');
-}
- _onPressButton2() {
- Alert.alert('Continue:')
+ type : "Point",
+ coordinates : coordinates,
+ gender : values.gender,
+ age : values.age,
+ race : values.race,
+ longhair : values.longhair,
+ longbeard : values.longbeard,
+ extra : (values.extra) ? values.extra : ""
+ }),
+ });
+ //// TODO: handle the response from the serve and decide what to display
+ //// based on that
+ //// for now just to back to the home page
+ alert("Thank you for your contribution.");
+ this.props.navigation.navigate('GreetingPage');
}
- constructor(props) {
- super(props);
- this.state = {
- age: '',
- appearance: '',
- injured: false,
- why: ''
- };
+
+ handleSubmit() {
+ var value = this.refs.form.getValue();
+ if (value) {
+ this._onSubmitPress(value);
+ this.clearForm();
+ }
+ }
+
+ onChange(value) {
+ this.setState({ value });
+ }
+
+ clearForm() {
+ this.setState({ value: null });
}
render() {
return (
- <View style={{padding: 40}}>
- <Text>Age:</Text>
- <Input
- style={{height: 40}}
- placeholder="Estimate is fine"
- onChangeText={(age) => this.setState({age})}
- />
- <Text>Appearance:</Text>
- <Input
- style={{height: 40}}
- placeholder="What are they wearing?"
- onChangeText={(appearance) => this.setState({appearance})}
- />
- <CheckBox
- center
- title="Are they injured?"
- checked={this.state.injured}
- onPress={() => this.setState({ injured: !this.state.injured })}
- />
- <Text>Reason for help?</Text>
- <Input
- style={{height: 40}}
- placeholder="Please keep it short"
- onChangeText={(why) => this.setState({why})}
- />
- <View style={{padding: 40}}>
- <Button
- buttonStyle={{backgroundColor:"green"}}
- onPress={() => this._onPressButton1()}
- title="Submit!"
- />
- <Button
- containerStyle={{paddingTop: 10}}
- onPress={() => this.props.navigation.navigate('GreetingPage')}
- title="Go Back"
- />
+ <KeyboardAvoidingView
+ style={styles.container}
+ behavior="position"
+ >
+ <Form
+ ref="form" // assign a reference
+ type={Report}
+ options={options}
+ value={this.state.value}
+ onChange={this.onChange.bind(this)}
+ />
+ <View style={{flexDirection: 'row'}}>
+ <View style={{flex:1 , marginRight:10}} >
+ <Button title="Back" onPress={() => this.props.navigation.navigate('GreetingPage')} />
+ </View>
+ <View style={{flex:1}} >
+ <Button title="Submit" color="#841584" onPress={() => this.handleSubmit()} />
+ </View>
</View>
- </View>
+ </KeyboardAvoidingView>
);
}
}
+
+const styles = StyleSheet.create({
+ container: {
+ justifyContent: 'center',
+ marginTop: 50,
+ padding: 20,
+ backgroundColor: '#ffffff',
+ },
+});
diff --git a/web/models/mockpoints.js b/web/models/mockpoints.js
index ff5e771..ac9ecd4 100644
--- a/web/models/mockpoints.js
+++ b/web/models/mockpoints.js
@@ -5,10 +5,12 @@ var Schema = mongoose.Schema;
var dumbJsonSchema = new Schema({
type: String,
coordinates: Array,
- ageRange: String,
- clothingDescription: String,
- isInjured: Boolean,
- reasonForHelp: String
+ gender: String,
+ age: String,
+ race: String,
+ longhair: Boolean,
+ longbeard: Boolean,
+ extra: String
});
// Mongoose Model definition
diff --git a/web/models/points.js b/web/models/points.js
index 3a07c82..d27f2a7 100644
--- a/web/models/points.js
+++ b/web/models/points.js
@@ -5,10 +5,12 @@ var Schema = mongoose.Schema;
var JsonSchema = new Schema({
type: String,
coordinates: Array,
- ageRange: String,
- clothingDescription: String,
- isInjured: Boolean,
- reasonForHelp: String
+ gender: String,
+ age: String,
+ race: String,
+ longhair: Boolean,
+ longbeard: Boolean,
+ extra: String
});
// Mongoose Model definition
diff --git a/web/public/javascripts/map.js b/web/public/javascripts/map.js
index 6a82dde..e2b4fc1 100644
--- a/web/public/javascripts/map.js
+++ b/web/public/javascripts/map.js
@@ -11,6 +11,15 @@ let orangeIcon = L.icon({
})
+const Races = {
+ "White" : "European or White",
+ "eAsian" : "East Asian",
+ "sAsian" : "South Asian",
+ "Black" : "Black or African American",
+ "Aboriginal": "Aboriginal",
+ "Other" : "Other",
+};
+
function plotPointsOnMap(points) {
L.geoJson(points, {
pointToLayer: function (feature, latlng) {
@@ -18,7 +27,7 @@ function plotPointsOnMap(points) {
latlngbounds.extend(latlng);
return L.marker(latlng);
}
- }).on('click', showDetails).addTo(map)
+ }).on('click', showDetails).addTo(map);
// rezoom the map so that all the markers fit in the view, add 20% padding so
// that marker dont cut off
@@ -29,6 +38,7 @@ function plotPointsOnMap(points) {
// e is the event info
function showDetails(e) {
// layer.feature.geometry gives you access to all the fields
+<<<<<<< HEAD
let layer = e.layer
console.log(e)
//layer._icon.src = '../assets/blue-icon.png'
@@ -39,57 +49,57 @@ function showDetails(e) {
console.log(currPoint)
let sideBar = document.getElementById('sidebar')
+=======
+ let layer = e.layer;
+
+ let sideBar = document.getElementById('sidebar');
+>>>>>>> master
if (getComputedStyle(sideBar).visibility === 'hidden') {
- sideBar.style.visibility = 'visible'
+ sideBar.style.visibility = 'visible';
}
- let point = document.getElementById('point')
-
- //get the previous input text from the previous point
- let prevUserInput = point.getElementsByClassName('user-input')
-
- // remove the previous point text
- while (prevUserInput.length !== 0) {
- prevUserInput[0].parentNode.removeChild(prevUserInput[0])
+ // remove the previous report text
+ let report = document.getElementById('report');
+ let prevUserInputs = report.getElementsByClassName('user-input');
+ for (let i=0; i < prevUserInputs.length; i++) {
+ prevUserInputs[i].innerHTML = "";
}
- let pointBreaks = point.getElementsByClassName('point-break')
+ // put gender of the person
+ let genderTextSpan = document.getElementById('report-gender');
+ genderTextSpan.className = 'user-input';
+ let genderText = document.createTextNode(layer.feature.geometry['gender']);
+ genderTextSpan.appendChild(genderText);
// put age range of person
- let ageRangeTextSpan = document.createElement('span')
- ageRangeTextSpan.className = 'user-input'
- let ageRangeText = document.createTextNode(layer.feature.geometry['ageRange'])
- ageRangeTextSpan.appendChild(ageRangeText)
- pointBreaks[0].parentNode.insertBefore(ageRangeTextSpan, pointBreaks[0])
-
- // put clothing description of person
- let clothingDescTextSpan = document.createElement('span')
- clothingDescTextSpan.className = 'user-input'
- let clothingDescText = document.createTextNode(layer.feature.geometry['clothingDescription'])
- clothingDescTextSpan.appendChild(clothingDescText)
- pointBreaks[1].parentNode.insertBefore(clothingDescTextSpan, pointBreaks[1])
-
- // put whether person is injured or not
- let isInjured = layer.feature.geometry['isInjured']
- let injurySpan = document.createElement('span')
- injurySpan.className = 'user-input'
- if (isInjured) {
- injurySpan.appendChild(document.createTextNode('Injured'))
- injurySpan.style.color = 'red'
- }
- else {
- injurySpan.appendChild(document.createTextNode('Not injured'))
- injurySpan.style.color = 'green'
- }
- pointBreaks[2].parentNode.insertBefore(injurySpan, pointBreaks[2])
-
- // put reason for help
- let helpReasonTextSpan = document.createElement('span')
- helpReasonTextSpan.className = 'user-input'
- let helpReasonText = document.createTextNode(layer.feature.geometry['reasonForHelp'])
- helpReasonTextSpan.appendChild(helpReasonText)
- point.appendChild(helpReasonTextSpan)
+ let ageRangeTextSpan = document.getElementById('report-age-range');
+ ageRangeTextSpan.className = 'user-input';
+ let ageRangeText = document.createTextNode(
+ " ~ " + layer.feature.geometry['age'] + " years");
+ ageRangeTextSpan.appendChild(ageRangeText);
+
+ // put race of person
+ let raceTextSpan = document.getElementById('report-race');
+ raceTextSpan.className = 'user-input';
+ let raceText = document.createTextNode(
+ Races[layer.feature.geometry['race']]
+ );
+ raceTextSpan.appendChild(raceText);
+
+ // put other attributes
+ let otherAttrTextSpan = document.getElementById('report-other');
+ otherAttrTextSpan.className = 'user-input';
+ otherAttrTextSpan.innerHTML =
+ "Long hair? " + (layer.feature.geometry['longhair'] ? "Yes" : "No") +
+ "<br>" +
+ "Long beard? " + (layer.feature.geometry['longbeard'] ? "Yes" : "No");
+
+ // put extra info
+ let extraTextSpan = document.getElementById('report-distinctive');
+ extraTextSpan.className = 'user-input';
+ let extraText = document.createTextNode(layer.feature.geometry['extra']);
+ extraTextSpan.appendChild(extraText);
// let pendingBtn = document.getElementById('pending-btn')
// pendingBtn.addEventListener('click', markAsPending)
diff --git a/web/public/stylesheets/style.css b/web/public/stylesheets/style.css
index d8618cd..ece8da4 100644
--- a/web/public/stylesheets/style.css
+++ b/web/public/stylesheets/style.css
@@ -21,8 +21,6 @@ a {
left: 5px;
z-index: 2;
width: 350px;
- height: 275px;
- max-height: 275px;
position: relative;
float: left;
background-color: lightgrey;
@@ -34,11 +32,11 @@ a {
margin-top: 10px;
margin-bottom: 10px;
}
-#point-container {
- overflow: auto;
- max-height: 195px;
+#report-container {
+ overflow: auto;
+ max-height: 500px;
}
-#point {
+#report {
background-color: #0CC5EA;
width: 90%;
margin-left: auto;
@@ -46,9 +44,27 @@ a {
margin-bottom: 5px;
padding: 5px;
border-radius: 10px;
+ display: flex;
+ justify-content: flex-end;
+ flex-direction: column;
+}
+#report strong {
+ font-size: 22px;
+ font-style: italic;
+ margin: auto;
+ padding: 5px;
+}
+#report span {
+ background-color: white;
+ padding: 15px;
+ border-radius: 10px;
+ font-weight: lighter;
+ font-size: large;
+ text-align: center;
+ overflow-wrap: break-word;
+ word-wrap: break-word;
}
#close-btn-container {
- position: fixed;
height: 30px;
top: 250px;
background-color: lightgrey;
@@ -64,4 +80,4 @@ a {
margin-right: 20px;
border-radius: 10px;
width: 100px;
-} \ No newline at end of file
+}
diff --git a/web/routes/index.js b/web/routes/index.js
index b2ba91f..23fcf36 100644
--- a/web/routes/index.js
+++ b/web/routes/index.js
@@ -23,14 +23,7 @@ router.get('/map', function(req,res) {
});
router.post('/mobilerequest', function(req, res) {
- var data = {
- 'coordinates': req.body.coordinates,
- 'type': req.body.type,
- 'isInjured': req.body.isInjured,
- 'reasonForHelp': req.body.reasonForHelp,
- 'ageRange': req.body.ageRange,
- 'clothingDescription': req.body.clothingDescription
- }
+ var data = req.body;
Points.save_request(data, function(err, result) {
if (err) {
console.log(`err inserting mobile request into db: ${err}`);
@@ -44,7 +37,7 @@ router.post('/mobilerequest', function(req, res) {
res.status(200).send({'status': 'success', 'data': data});
}
});
-
+
});
diff --git a/web/views/map.pug b/web/views/map.pug
index f5837b6..484899c 100644
--- a/web/views/map.pug
+++ b/web/views/map.pug
@@ -2,15 +2,18 @@ extends layout.pug
block content
#sidebar
h1#details-header Details
- #point-container
- #point
- strong Age range:
- br.point-break
- strong Clothing description:
- br.point-break
- strong Injury status:
- br.point-break
- strong Reason for help:
+ #report-container
+ #report
+ strong Gender
+ span#report-gender
+ strong Age Range
+ span#report-age-range
+ strong Race
+ span#report-race
+ strong Other Attributes
+ span#report-other
+ strong Distinctive Features
+ span#report-distinctive
#close-btn-container
button#close-btn Close