diff --git a/.babelrc b/.babelrc
index 2bcd546..76bf50e 100644
--- a/.babelrc
+++ b/.babelrc
@@ -1,5 +1,6 @@
{
"presets": ["babel-preset-expo"],
+ "plugins": ["transform-decorators-legacy"],
"env": {
"development": {
"plugins": ["transform-react-jsx-source"]
diff --git a/.gitignore b/.gitignore
index 9f9e17e..d3fce32 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
node_modules/**/*
.expo/*
npm-debug.*
+tmp*
diff --git a/App.js b/App.js
index a4c6ec1..8226ea9 100644
--- a/App.js
+++ b/App.js
@@ -1,169 +1,13 @@
-import React from 'react';
-import { StyleSheet, Text, View, TouchableOpacity } from 'react-native';
-import { MapView } from 'expo';
-let id = 0;
+import React, { Component } from 'react';
+import { Navigator } from './Navigator';
-export default class App extends React.Component {
+export default class App extends Component {
render() {
+ console.log('Starting anew....');
return (
-
+
);
}
}
-class MapWithMarkers extends React.Component {
- constructor(props) {
- super(props);
- this.state = this.getInitialState();
- }
-
- getInitialState() {
- return {
- region: {
- latitude: 42.324094,
- longitude: -71.384572,
- latitudeDelta: 0.0922,
- longitudeDelta: 0.0421,
- },
- markers: [
- {
- coordinate: {
- latitude: 42.324094,
- longitude: -71.384572,
- },
- key: id++,
- title: 'Home 1',
- description: 'backyard',
- color: '#ababac',
- },
- {
- coordinate: {
- latitude: 42.3009,
- longitude: -71.3842,
- },
- key: id++,
- title: 'Natick Mall',
- description: 'Natick Mall',
- color: '#ababac',
- },
- ],
- popupIsOpen: false,
- };
- }
-
- onRegionChange(region) {
- this.setState({ region });
- }
-
- onMapPress(e) {
- this.setState({
- markers: [
- ...this.state.markers,
- {
- coordinate: e.nativeEvent.coordinate,
- key: id++,
- color: '#ababab',
- },
- ],
- });
- }
-
- showMarkerCallout = (marker) => {
- marker.showCallout();
- }
-
- hideMarkerCallout = (marker) => {
- marker.hideCallout();
- }
-
- openMarker = (marker) => {
- this.setState({
- popupIsOpen: true,
- marker,
- });
- }
-
- closeMarker = () => {
- this.setState({
- popupIsOpen: false,
- });
- }
-
- render() {
- return (
-
- this.onRegionChange(r)}
- onPress={(e) => this.onMapPress(e)}
- >
- {this.state.markers.map(marker => (
- showMarkerCallout(marker) }
- onCalloutPress={ (marker) => hideMarkerCallout(marker) }
- >
-
-
- Example Callout
-
-
-
- ))}
-
-
- this.setState({ markers: [] })}
- style={styles.bubble}
- >
- Tap to clear markers
-
-
-
- );
- }
-}
-
-MapWithMarkers.propTypes = {
- provider: MapView.ProviderPropType,
-};
-
-const styles = StyleSheet.create({
- container: {
- ...StyleSheet.absoluteFillObject,
- justifyContent: 'flex-end',
- alignItems: 'center',
- },
- map: {
- ...StyleSheet.absoluteFillObject,
- },
- bubble: {
- backgroundColor: 'rgba(255,255,255,0.7)',
- paddingHorizontal: 18,
- paddingVertical: 12,
- borderRadius: 20,
- },
- latlng: {
- width: 200,
- alignItems: 'stretch',
- },
- button: {
- width: 80,
- paddingHorizontal: 12,
- alignItems: 'center',
- marginHorizontal: 10,
- },
- buttonContainer: {
- flexDirection: 'row',
- marginVertical: 20,
- backgroundColor: 'transparent',
- },
- plainView: {
- width: 60,
- },
-});
\ No newline at end of file
+App.router = Navigator.router;
\ No newline at end of file
diff --git a/MarkerPopup.js b/MarkerPopup.js
index f3d110f..69a8f39 100644
--- a/MarkerPopup.js
+++ b/MarkerPopup.js
@@ -1,29 +1,160 @@
/*
-Attribution:
-Code from http://rationalappdev.com/movie-tickets-booking-app-with-react-native/#data
+Attribution: http://rationalappdev.com/movie-tickets-booking-app-with-react-native/#data
*/
-
import React, { Component } from 'react';
+import { PropTypes } from 'prop-types';
import {
+ Alert,
Animated,
Dimensions,
+ Image,
+ LayoutAnimation,
+ PanResponder,
+ Platform,
+ PlatformOSType,
+ ScrollView,
StyleSheet,
+ Switch,
Text,
+ TouchableHighlight,
+ TouchableOpacity,
TouchableWithoutFeedback,
+ UIManager,
View
} from 'react-native';
+import Icon from 'react-native-vector-icons/FontAwesome';
+import Service from './Service';
const { width, height } = Dimensions.get('window');
+//Set default popup height to 67% of the screen height
+const defaultHeight = height * 0.67;
+//To support layout animation on Android, though not yet working
+UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true);
export default class MarkerPopup extends Component {
+ static propTypes = {
+ isOpen: PropTypes.bool.isRequired,
+ selectedMarker: PropTypes.object,
+ onClose: PropTypes.func,
+ }
+
state = {
- position: new Animated.Value(this.props.isOpen ? 0 : height),
- visible: this.props.isOpen,
+ // Animates slide ups and downs when popup open or closed
+ position: new Animated.Value(this.props.isOpen ? 0 : height),
+ // Backdrop opacity
+ opacity: new Animated.Value(0),
+ // Popup height that can be changed by pulling it up or down
+ popupHeight: defaultHeight,
+ // Expanded mode with bigger poster flag
+ expanded: false,
+ // Visibility flag
+ visible: this.props.isOpen,
+ lightSwitch: false // this.props.selectedMarker ? this.getSwitchValue(this.props.selectedMarker._id) : false,
+ //isActive: true, //this.props.selectedMarker.isActive, ARGH - CANNOT GET THIS TO WORK
+ //hasWarning: false, // this.props.selectedMarker.hasWarning, ARGH - CANNOT GET THIS TO WORK
};
+ // When user starts pulling popup previous height gets stored here
+ // to help us calculate new height value during and after pulling
+ _previousHeight = 0
+
+ componentWillMount() {
+ console.log('calling Component Will Mount');
+ console.log('marker popup state is currently' + JSON.stringify(this.state));
+ // Initialize PanResponder to handle move gestures
+ this._panResponder = PanResponder.create({
+ onStartShouldSetPanResponder: (evt, gestureState) => true,
+ onMoveShouldSetPanResponder: (evt, gestureState) => {
+ const { dx, dy } = gestureState;
+ // Ignore taps
+ if (dx !== 0 && dy === 0) {
+ return true;
+ }
+ return false;
+ },
+ onPanResponderGrant: (evt, gestureState) => {
+ // Store previous height before user changed it
+ console.log('Gesture started');
+ this._previousHeight = this.state.popupHeight;
+ },
+ onPanResponderMove: (evt, gestureState) => {
+ // Pull delta and velocity values for y axis from gestureState
+ const { dy, vy } = gestureState;
+ // Subtract delta y from previous height to get new height
+ let newHeight = this._previousHeight - dy;
+ console.log('New height is ' + newHeight);
+
+ // Animate height change so it looks smooth
+ LayoutAnimation.easeInEaseOut(); //NOT WORKING ON ANDROID
+
+ // Switch to expanded mode if popup pulled up above 80% mark
+ if (newHeight > height - height / 5) {
+ console.log('height raised- should fully expand');
+ this.setState({ expanded: true });
+ } else {
+ this.setState({ expanded: false });
+ }
+
+ // Expand to full height if pulled up rapidly
+ if (vy < -0.75) {
+ this.setState({
+ expanded: true,
+ popupHeight: height
+ });
+ }
+
+ // Close if pulled down rapidly
+ else if (vy > 0.75) {
+ this.props.onClose();
+ }
+ // Close if pulled below 75% mark of default height
+ else if (newHeight < defaultHeight * 0.75) {
+ this.props.onClose();
+ }
+ // Limit max height to screen height
+ else if (newHeight > height) {
+ this.setState({ popupHeight: height });
+ }
+ else {
+ this.setState({ popupHeight: newHeight });
+ }
+ },
+ onPanResponderTerminationRequest: (evt, gestureState) => {
+ console.log('gesture failed to complete');
+ return true;
+ },
+ onPanResponderRelease: (evt, gestureState) => {
+ const { dy } = gestureState;
+ const newHeight = this._previousHeight - dy;
+ console.log('New height set: ' + newHeight);
+ // Close if pulled below default height
+ if (newHeight < defaultHeight) {
+ this.props.onClose();
+ }
+
+ // Update previous height
+ this._previousHeight = this.state.popupHeight;
+
+ },
+ onShouldBlockNativeResponder: (evt, gestureState) => {
+ // Returns whether this component should block native components from becoming the JS
+ // responder. Returns true by default. Is currently only supported on android.
+ return true;
+ },
+ });
+ }
+
// Handle isOpen changes to either open or close popup
componentWillReceiveProps(nextProps) {
+ const {
+ selectedMarker,
+ } = nextProps;
+
+ const { _id } = selectedMarker || {};
+ console.log('Ant _id: ' + _id);
+
+ console.log('calling Component Will Receive Props on nextProps: ' + JSON.stringify({_id}) );
// isOpen prop changed to true from false
if (!this.props.isOpen && nextProps.isOpen) {
this.animateOpen();
@@ -32,45 +163,204 @@ export default class MarkerPopup extends Component {
else if (this.props.isOpen && !nextProps.isOpen) {
this.animateClose();
}
+
+ if (_id)
+ {
+ this.getSwitchValue(_id);
+ console.log('updating the switch value during componentwill receive props' );
+ }
+
}
// Open popup
animateOpen() {
// Update state first
this.setState({ visible: true }, () => {
- // And slide up
- Animated.timing(
- this.state.position, { toValue: 0 } // top of the screen
- ).start();
+ Animated.parallel([
+ // Animate opacity
+ Animated.timing(
+ this.state.opacity, { toValue: 0.5 } // semi-transparent
+ ),
+ // And slide up
+ Animated.timing(
+ this.state.position, { toValue: 0 } // top of the screen
+ ),
+ ]).start();
});
}
// Close popup
animateClose() {
- // Slide down
- Animated.timing(
- this.state.position, { toValue: height } // bottom of the screen
- ).start(() => this.setState({ visible: false }));
+ Animated.parallel([
+ // Animate opacity
+ Animated.timing(
+ this.state.opacity, { toValue: 0 } // transparent
+ ),
+ // Slide down
+ Animated.timing(
+ this.state.position, { toValue: height } // bottom of the screen
+ ),
+ ]).start(() => this.setState({
+ // Reset to default values
+ popupHeight: defaultHeight,
+ expanded: false,
+ visible: false,
+ }));
+ }
+
+ getHeaderIcon() {
+ if (this.state.expanded) {
+ return
+ }
+ return ;
+ }
+
+ onWarningPress() {
+ console.log('you tapped the signal button');
+ Alert.alert('You tapped the Warning button! TODO- add what needs attention here.');
+ }
+
+ lightSwitchHandler(id, value) {
+ //this.setState({ lightSwitch: value });
+ (value) ? str = 'on' : str = 'off';
+ Service.handleLightSwitch(id, str)
+ .then((data) => {
+ console.log('light switch turned ' + str);
+ this.setState({ lightSwitch: value });
+ })
+ .catch((err) => { console.error('error turning light ' + str + ': ' + err)})
+ //need to map value to on/off
+ //TODO - callback to model to set light switch value; set state once Promise is resolved
+ }
+
+ //TODO - callback to model to get light switch value
+ // return true means light (switch) is on; return false means light (switch) is off
+ getSwitchValue(id) {
+ console.log('checking light switch value with id: ' + id);
+ Service.handleLightSwitch(id, 'status')
+ .then((data) => {
+ console.log('Light switch value is: ' + JSON.stringify(data) + ' and non-string form ' + data);
+ this.setState({ lightSwitch: data });
+ //return data;
+ })
+ .catch((err) => {
+ console.error('error getting light status: ' + err);
+ //return false;
+ })
+ //return false;
}
render() {
// Render nothing if not visible
+ const {
+ selectedMarker,
+ } = this.props;
+
+ const { name, _id, description, dateFirstOnline, dateLastServiced, hasWarning, status } = selectedMarker || {};
+ console.log('Ant ' + JSON.stringify({name}) + ' with _id: ' + JSON.stringify({_id}) + ' Has Warning: ' + {hasWarning}.hasWarning + ' and Ant isActive: ' + {status}.status );
+ if (!_id) {
+ return null;
+ }
+
if (!this.state.visible) {
return null;
}
+ //this.setState({ lightSwitch: this.getSwitchValue(_id) });
+ //this.state.lightSwitch = this.getSwitchValue(_id);
+ console.log('has light switch value: ' + this.state.lightSwitch);
+ //console.log('has warning type: ' + typeof {hasWarning} );
+ //console.log('has warning compare: ' + {hasWarning} == 'true');
+ var image = require('./assets/icons/loading-icon.png');
+ //var hasWarningString = {hasWarning};
+ //console.log(hasWarningString);
+
+
return (
{/* Closes popup if user taps on semi-transparent backdrop */}
-
+
- Marker Popup
+
+
+
+
+
+
+
+
+ {
+ ({status}.status === 'Active') ?
+ ( ACTIVE)
+ :
+ ( INACTIVE )
+ }
+ { ( {hasWarning}.hasWarning ) &&
+
+
+
+ }
+
+
+
+
+
+
+ {name}
+ {description}
+
+
+
+
+
+
+
+
+
+ Light:
+ this.lightSwitchHandler(_id, value)} value={this.state.lightSwitch} />
+
+
+ Temperature:
+ 80{'\u2109'}
+
+
+ Date Last Serviced:
+ {dateLastServiced}
+
+
+ Date First Online:
+ {dateFirstOnline}
+
+
+ Logs:
+ Lorem ipsum dolor sit amet,
+ consectetur adipiscing elit, sed do eiusmod tempor
+ incididunt ut labore et dolore magna aliqua.
+ Ut enim ad minim veniam, quis nostrud exercitation ullamco
+ laboris nisi ut aliquip ex ea commodo consequat.
+ Duis aute irure dolor in reprehenderit in voluptate
+ velit esse cillum dolore eu fugiat nulla pariatur.
+ Excepteur sint occaecat cupidatat non proident,
+ sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+
+ ...More
+
+
+
+
);
@@ -89,11 +379,80 @@ const styles = StyleSheet.create({
backdrop: {
...StyleSheet.absoluteFillObject, // fill up all screen
backgroundColor: 'black',
- opacity: 0.5,
+ //opacity: 0.5, // set dynamically using Animated
},
// Popup
modal: {
- height: height / 2, // take half of screen height
+ //height: height / 2, // set dynamically using Animated
backgroundColor: 'white',
},
+ content: {
+ flex: 1,
+ margin: 5,
+ marginBottom: 0,
+ },
+ markerContainer: {
+ flex: 1, // take up all available space
+ marginBottom: 20,
+ justifyContent: 'flex-start',
+ },
+ markerGripper: {
+ flexDirection: 'row',
+ justifyContent: 'center',
+ marginBottom: 2,
+ },
+ summary: {
+ margin: 5,
+ //flex: 1,
+ height: 90,
+ flexDirection: 'row',
+ alignItems: 'center',
+ },
+ title: {
+ paddingLeft: 10,
+ paddingRight: 10,
+ fontSize: 20,
+ },
+ description: {
+ paddingLeft: 10,
+ paddingRight: 10,
+ color: 'gray',
+ fontSize: 14,
+ },
+ image: {
+ borderRadius: 5, // rounded corners
+ width: 80,
+ height: 80,
+ },
+ markerInnerBox: {
+ margin: 5,
+ borderColor: '#BBBBBB',
+ borderWidth: 1,
+ padding: 5,
+ paddingRight: 10,
+ paddingLeft: 10,
+ borderRadius: 10,
+ },
+ markerQuickies2: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ height: 38,
+ },
+ markerQuickies: {
+ flexDirection: 'row',
+ justifyContent: 'flex-start',
+ paddingTop: 5,
+ paddingLeft: 10,
+ paddingRight: 10,
+ },
+ markerLineItems: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ //fontSize: 16, //cannot use within a ScrollView, oddly
+ paddingTop: 10,
+ paddingBottom: 10,
+ borderBottomWidth: 1,
+ borderBottomColor: 'gray',
+ margin: 5,
+ }
});
\ No newline at end of file
diff --git a/data.js b/data.js
index a5d22c9..65e2b68 100644
--- a/data.js
+++ b/data.js
@@ -1,22 +1,61 @@
-const assetType = [ 'spoke', 'hub', 'chargingSta', 'source'];
+const assetType = [ 'ant', 'gateway', 'service', 'client'];
-export const nodes = [
+export const ants = [
{
- id: 'a001',
+ _id: 'a001',
name: 'pizeroW1',
description: 'lake road backyard',
countryCode: 'us',
- lat: 42.31,
- long: -71.37,
- assetType: 'spoke',
+ coordinate: {
+ latitude: 42.324094,
+ longitude: -71.384572,
+ },
+ latitude: 42.324094,
+ longitude: -71.384572,
+ assetType: 'ant',
+ isActive: true,
+ status: 'Active',
+ radius: 50, //ft radius coverage
+ dateFirstOnline: '9/10/2017',
+ dateLastServiced: '9/10/2017',
+ hasWarning: true,
},
{
- id: 'a002',
+ _id: 'a002',
name: 'pizeroW2',
description: 'lake road frontyard',
countryCode: 'us',
- lat: 42.324094,
- long: -71.384572,
- assetType: 'spoke',
+ coordinate: {
+ latitude: 42.324109,
+ longitude: -71.384672,
+ },
+ latitude: 42.324109,
+ longitude: -71.384672,
+ assetType: 'ant',
+ isActive: true,
+ status: 'Active',
+ radius: 50, //ft radius coverage
+ dateFirstOnline: '9/10/2017',
+ dateLastServiced: '9/10/2017',
+ hasWarning: false,
+ },
+ {
+ _id: 'a003',
+ name: 'Natick Mall pizero1',
+ description: 'Natick Mall',
+ countryCode: 'us',
+ coordinate: {
+ latitude: 42.3009,
+ longitude: -71.3842,
+ },
+ latitude: 42.3009,
+ longitude: -71.3842,
+ assetType: 'ant',
+ isActive: false,
+ status: 'Recommended',
+ radius: 0, //ft radius coverage- only for active ants
+ dateFirstOnline: '9/10/2017',
+ dateLastServiced: '9/10/2017',
+ hasWarning: false,
},
];
\ No newline at end of file
diff --git a/package.json b/package.json
index 0415a3a..df28fde 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "lattice",
"version": "0.0.0",
- "description": "Hello Expo!",
+ "description": "prototype app",
"author": "Jessica Milan, Tech Behind The Scenes LLC",
"private": true,
"main": "node_modules/expo/AppEntry.js",
@@ -9,6 +9,13 @@
"expo": "^20.0.0",
"react": "16.0.0-alpha.12",
"react-native": "https://github.com/expo/react-native/archive/sdk-20.0.0.tar.gz",
- "react-native-maps": "^0.16.3"
+ "react-native-elements": "^0.16.0",
+ "react-native-maps": "^0.16.3",
+ "react-navigation": "^1.0.0-beta.11",
+ "react-redux": "^5.0.6",
+ "redux": "^3.7.2"
+ },
+ "devDependencies": {
+ "babel-plugin-transform-decorators-legacy": "^1.3.4"
}
}