From c72062567336c554170c9f0e27e5423e669c7efb Mon Sep 17 00:00:00 2001 From: techbehindthescenes Date: Mon, 2 Oct 2017 17:17:28 -0400 Subject: [PATCH] more initial commits --- .babelrc | 1 + .gitignore | 1 + App.js | 168 +-------------------- MarkerPopup.js | 393 ++++++++++++++++++++++++++++++++++++++++++++++--- data.js | 59 ++++++-- package.json | 11 +- 6 files changed, 442 insertions(+), 191 deletions(-) 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" } }