From d646b9477b5e4a84a1d737dfb04c77fb23345c27 Mon Sep 17 00:00:00 2001 From: John Lyon-Smith Date: Fri, 6 Apr 2018 11:35:23 -0700 Subject: [PATCH] Added swipable list to work items list --- .prettierrc | 3 + mobile/package-lock.json | 5 + mobile/package.json | 1 + mobile/src/API.js | 3 +- mobile/src/Auth/DefaultRoute.js | 2 +- mobile/src/Auth/Login.js | 145 ++++++++++++------- mobile/src/WorkItem/WorkItem.js | 189 ++++++++++++++---------- mobile/src/WorkItem/WorkItemList.js | 213 +++++++++++++++++++++++----- website/src/Auth/Login.js | 1 - 9 files changed, 394 insertions(+), 168 deletions(-) create mode 100644 .prettierrc diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..d8b7432 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +{ + "semi": false, +} \ No newline at end of file diff --git a/mobile/package-lock.json b/mobile/package-lock.json index 476c6ab..b23b6f8 100644 --- a/mobile/package-lock.json +++ b/mobile/package-lock.json @@ -5520,6 +5520,11 @@ "react-native-animatable": "1.2.4" } }, + "react-native-swipe-list-view": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/react-native-swipe-list-view/-/react-native-swipe-list-view-1.0.7.tgz", + "integrity": "sha512-JVWEoFFEn/qSWHlWaPyz7HdoCCyHr/ifxOOmOhs/s4rmm+d3C7suJucBhllbYcwSG7s19WqtV2Ia0iEl+pK4Gg==" + }, "react-proxy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/react-proxy/-/react-proxy-1.1.8.tgz", diff --git a/mobile/package.json b/mobile/package.json index babe66d..d0752c7 100644 --- a/mobile/package.json +++ b/mobile/package.json @@ -29,6 +29,7 @@ "react-native-keyboard-spacer": "^0.4.1", "react-native-maps": "^0.20.1", "react-native-modal": "^5.4.0", + "react-native-swipe-list-view": "^1.0.7", "react-router-native": "^4.2.0", "react-viro": "^2.4.0", "socket.io-client": "^2.0.4" diff --git a/mobile/src/API.js b/mobile/src/API.js index cce99b5..7107cbc 100644 --- a/mobile/src/API.js +++ b/mobile/src/API.js @@ -57,7 +57,8 @@ class API extends EventEmitter { this.user = user this.connectSocket() this.emit('login') - }).catch(() => { + }).catch((err) => { + console.error(err) AsyncStorage.removeItem(authTokenName) this.token = null this.user = {} diff --git a/mobile/src/Auth/DefaultRoute.js b/mobile/src/Auth/DefaultRoute.js index 475a030..f915466 100644 --- a/mobile/src/Auth/DefaultRoute.js +++ b/mobile/src/Auth/DefaultRoute.js @@ -3,5 +3,5 @@ import { Route, Redirect } from 'react-router-native' export const DefaultRoute = () => { // NOTE: When working on the app, change this to the page you are working on - return ()} /> + return ()} /> } diff --git a/mobile/src/Auth/Login.js b/mobile/src/Auth/Login.js index 4cfe074..aa349ea 100644 --- a/mobile/src/Auth/Login.js +++ b/mobile/src/Auth/Login.js @@ -1,22 +1,31 @@ -import React from 'react'; -import { Platform, StyleSheet, Text, Image, Switch, TextInput, - KeyboardAvoidingView, View, Button } from 'react-native' -import { MessageModal } from '../Modal' -import logoImage from './images/deighton.png' -import { FormBinder } from 'react-form-binder' -import { api } from '../API' -import { BoundSwitch, BoundInput, BoundButton } from '../ui' -import autobind from 'autobind-decorator' +import React from "react" +import { + Platform, + StyleSheet, + Text, + Image, + Switch, + TextInput, + View, + Button +} from "react-native" +import { MessageModal } from "../Modal" +import logoImage from "./images/deighton.png" +import { FormBinder } from "react-form-binder" +import { api } from "../API" +import { BoundSwitch, BoundInput, BoundButton } from "../ui" +import KeyboardSpacer from "react-native-keyboard-spacer" +import autobind from "autobind-decorator" export class Login extends React.Component { static bindings = { email: { alwaysGet: true, - isValid: (r, v) => (v !== '') + isValid: (r, v) => v !== "" }, password: { alwaysGet: true, - isValid: (r, v) => (v !== '') + isValid: (r, v) => v !== "" }, rememberMe: { alwaysGet: true, @@ -25,49 +34,49 @@ export class Login extends React.Component { }, login: { noValue: true, - isDisabled: (r) => (!(r.anyModified && r.allValid)) + isDisabled: r => !(r.anyModified && r.allValid) } } static styles = StyleSheet.create({ page: { - minHeight: '50%', + minHeight: "50%", flex: 1, - backgroundColor: '#fff', - alignItems: 'center', - justifyContent: 'center', + backgroundColor: "#fff", + alignItems: "center", + justifyContent: "center" }, logo: { - width: '50%' + width: "50%" }, inputRow: { paddingTop: 10, - width: '60%', - flexDirection: 'column', - justifyContent: 'flex-end', - alignItems: 'flex-start', + width: "60%", + flexDirection: "column", + justifyContent: "flex-end", + alignItems: "flex-start" }, switchRow: { paddingTop: 10, - width: '60%', - flexDirection: 'row', - justifyContent: 'flex-end', - alignItems: 'center' + width: "60%", + flexDirection: "row", + justifyContent: "flex-end", + alignItems: "center" }, buttonRow: { paddingTop: 10, - width: '60%', - flexDirection: 'row', - justifyContent: 'flex-end', - alignItems: 'center' + width: "60%", + flexDirection: "row", + justifyContent: "flex-end", + alignItems: "center" } }) constructor(props) { super(props) this.state = { - binder: new FormBinder({email: 'john@lyon-smith.org'}, Login.bindings), - messageModal: null, + binder: new FormBinder({ email: "john@lyon-smith.org" }, Login.bindings), + messageModal: null } } @@ -77,15 +86,20 @@ export class Login extends React.Component { let { history } = this.props if (obj) { - api.login(obj.email, obj.password, obj.rememberMe).then((user) => { - history.replace('/home') - }).catch((error) => { - this.setState({ messageModal: { - icon: 'hand', - message: 'Unable to login', - detail: error.message, - }}) - }) + api + .login(obj.email, obj.password, obj.rememberMe) + .then(user => { + history.replace("/home") + }) + .catch(error => { + this.setState({ + messageModal: { + icon: "hand", + message: "Unable to login", + detail: error.message + } + }) + }) } } @@ -98,28 +112,55 @@ export class Login extends React.Component { const { messageModal } = this.state return ( - - + + - + - + - + - + - + icon={messageModal ? messageModal.icon : ""} + message={messageModal ? messageModal.message : ""} + detail={messageModal ? messageModal.detail : ""} + onDismiss={messageModal && this.handleMessageDismiss} + /> + + ) } } diff --git a/mobile/src/WorkItem/WorkItem.js b/mobile/src/WorkItem/WorkItem.js index 6c48232..8eedbe4 100644 --- a/mobile/src/WorkItem/WorkItem.js +++ b/mobile/src/WorkItem/WorkItem.js @@ -1,4 +1,4 @@ -import React from 'react' +import React from "react" import { StyleSheet, View, @@ -9,9 +9,9 @@ import { KeyboardAvoidingView, Platform, TouchableOpacity -} from 'react-native' -import MapView, { Marker } from 'react-native-maps' -import { FormBinder } from 'react-form-binder' +} from "react-native" +import MapView, { Marker } from "react-native-maps" +import { FormBinder } from "react-form-binder" import { BoundInput, BoundButton, @@ -19,51 +19,54 @@ import { Icon, Header, PhotoButton, - BoundOptionStrip, -} from '../ui' -import { MessageModal } from '../Modal' -import autobind from 'autobind-decorator' -import { ifIphoneX, isIphoneX } from 'react-native-iphone-x-helper' -import KeyboardSpacer from 'react-native-keyboard-spacer' -import { api } from '../API' + BoundOptionStrip +} from "../ui" +import { MessageModal } from "../Modal" +import autobind from "autobind-decorator" +import { ifIphoneX, isIphoneX } from "react-native-iphone-x-helper" +import KeyboardSpacer from "react-native-keyboard-spacer" +import { api } from "../API" const styles = StyleSheet.create({ container: { - flexDirection: 'column', + flexDirection: "column", flexGrow: 1, - backgroundColor: '#DDDDDD', + backgroundColor: "#DDDDDD" }, panel: { - width: '94%', - backgroundColor: 'white', - alignSelf: 'center', - marginTop: '3%', - shadowColor: 'gray', + width: "94%", + backgroundColor: "white", + alignSelf: "center", + marginTop: "3%", + shadowColor: "gray", shadowOffset: { width: 2, height: 2 }, shadowRadius: 2, - shadowOpacity: .5, - padding: 10, + shadowOpacity: 0.5, + padding: 10 }, label: { fontSize: 14, - marginBottom: 4, + marginBottom: 4 } }) const workItemOptions = [ - { value: 'order', text: 'Work Order' }, - { value: 'inspection', text: 'Inspection' }, - { value: 'complaint', text: 'Complaint' } + { value: "order", text: "Work Order" }, + { value: "inspection", text: "Inspection" }, + { value: "complaint", text: "Complaint" } ] -const latLngToString = (lat, lng) => (`${Math.abs(lng).toFixed(4)}°${lng > 0 ? 'S' : 'N'}, ${Math.abs(lat).toFixed(4)}°${lat > 0 ? 'W' : 'E'}`) -const latLngStringToPoint = (str) => { - const parts = str.split(', ') +const latLngToString = (lat, lng) => + `${Math.abs(lng).toFixed(4)}°${lng > 0 ? "S" : "N"}, ${Math.abs(lat).toFixed( + 4 + )}°${lat > 0 ? "W" : "E"}` +const latLngStringToPoint = str => { + const parts = str.split(", ") return { - type: 'Point', + type: "Point", coordinates: [ new Number(parts[0].substring(0, parts[0].length - 2)), - new Number(parts[1].substring(0, parts[1].length - 2)), + new Number(parts[1].substring(0, parts[1].length - 2)) ] } } @@ -72,19 +75,19 @@ export class WorkItem extends React.Component { static bindings = { header: { noValue: true, - isDisabled: (r) => (!(r.anyModified && r.allValid)), + isDisabled: r => !(r.anyModified && r.allValid) }, location: { isValid: true, - isDisabled: true, + isDisabled: true }, details: { - isValid: (r, v) => (v !== ''), + isValid: (r, v) => v !== "" }, workItemType: { isValid: true, - initValue: 'order', - alwaysGet: true, + initValue: "order", + alwaysGet: true } } @@ -92,7 +95,7 @@ export class WorkItem extends React.Component { super(props) this.state = { binder: new FormBinder({}, WorkItem.bindings), - messageModal: null, + messageModal: null } } @@ -103,7 +106,7 @@ export class WorkItem extends React.Component { if (history.length > 1) { history.goBack() } else { - history.replace('/home') + history.replace("/home") } } @@ -115,25 +118,35 @@ export class WorkItem extends React.Component { obj.location = latLngStringToPoint(obj.location) if (!obj._id) { - api.createWorkItem(obj).then((workItem) => { - this.handleBackPress() - }).catch((error) => { - this.setState({ messageModal: { - icon: 'hand', - message: 'Unable to create work item', - detail: error.message, - }}) - }) + api + .createWorkItem(obj) + .then(workItem => { + this.handleBackPress() + }) + .catch(error => { + this.setState({ + messageModal: { + icon: "hand", + message: "Unable to create work item", + detail: error.message + } + }) + }) } else { - api.updateWorkItem(obj).then((workItem) => { - this.handleBackPress() - }).catch((error) => { - this.setState({ messageModal: { - icon: 'hand', - message: 'Unable to update work item', - detail: error.message, - }}) - }) + api + .updateWorkItem(obj) + .then(workItem => { + this.handleBackPress() + }) + .catch(error => { + this.setState({ + messageModal: { + icon: "hand", + message: "Unable to update work item", + detail: error.message + } + }) + }) } } @@ -146,7 +159,12 @@ export class WorkItem extends React.Component { handleRegionChange(region) { const { binder } = this.state - this.setState(binder.updateFieldValue('location', latLngToString(region.latitude, region.longitude))) + this.setState( + binder.updateFieldValue( + "location", + latLngToString(region.latitude, region.longitude) + ) + ) } render() { @@ -156,54 +174,75 @@ export class WorkItem extends React.Component { + name="header" + title="Work Item" + leftButton={{ icon: "back", onPress: this.handleBackPress }} + rightButton={{ icon: "done", onPress: this.handleDonePress }} + /> - + - + - + onRegionChange={this.handleRegionChange} + > + - + Pictures: - + - { isIphoneX ? : null } + {isIphoneX ? : null} + icon={messageModal ? messageModal.icon : ""} + message={messageModal ? messageModal.message : ""} + detail={messageModal ? messageModal.detail : ""} + onDismiss={messageModal && this.handleMessageDismiss} + /> ) diff --git a/mobile/src/WorkItem/WorkItemList.js b/mobile/src/WorkItem/WorkItemList.js index c5d921f..ca55658 100644 --- a/mobile/src/WorkItem/WorkItemList.js +++ b/mobile/src/WorkItem/WorkItemList.js @@ -1,53 +1,136 @@ -import React from 'react' -import { StyleSheet, View, TouchableOpacity, Image, FlatList, Text} from 'react-native' -import { Icon, Header } from '../ui' -import autobind from 'autobind-decorator' +import React from "react" +import { + StyleSheet, + View, + TouchableHighlight, + TouchableOpacity, + Image, + FlatList, + Text +} from "react-native" +import { Icon, Header } from "../ui" +import { MessageModal } from "../Modal" +import autobind from "autobind-decorator" +import { SwipeListView } from "react-native-swipe-list-view" +import { api } from "../API" const styles = StyleSheet.create({ container: { - height: '100%', - width: '100%', - justifyContent: 'flex-start', - backgroundColor: '#FFFFFF', - }, + height: "100%", + width: "100%", + justifyContent: "flex-start", + backgroundColor: "#FFFFFF" + } }) const data = [ - {key: '1', type: 'work', location: 'Ossington Ave. | 0.2 mi.', state: 'open', latlng: { latitude: 43.653226, longitude: -79.383184 } }, - {key: '2', type: 'inspection', location: 'Alexandre St. | 0.7 mi.', state: 'open', latlng: { latitude: 43.648118, longitude: 79.392636 }}, - {key: '3', type: 'complaint', location: 'Bay St. | 0.8 mi.', state: 'open', latlng: { latitude: 43.640168, longitude: -79.409373 }}, - {key: '4', type: 'work', location: 'Bloor St. | 1.2 mi.', state: 'open', latlng: { latitude: 43.633110, longitude: -79.415880 }}, - {key: '5', type: 'inspection', location: 'Blue Jays Way | 2.2 mi.', state: 'open', latlng: { latitude: 43.653526, longitude: -79.361385 }}, - {key: '6', type: 'complaint', location: 'Christie St. | 3.0 mi.', state: 'open', latlng: { latitude: 43.663870, longitude: -79.383705 }}, - {key: '7', type: 'work', location: 'Cummer Ave. | 4.2 mi.', state: 'open', latlng: { latitude: 43.659166, longitude: -79.391350 }}, - {key: '8', type: 'complaint', location: 'Danforth Ave. | 4.7 mi.', state: 'open', latlng: { latitude: 43.663538, longitude: -79.423212 }}, + { + key: "1", + type: "work", + location: "Ossington Ave. | 0.2 mi.", + state: "open", + latlng: { latitude: 43.653226, longitude: -79.383184 } + }, + { + key: "2", + type: "inspection", + location: "Alexandre St. | 0.7 mi.", + state: "open", + latlng: { latitude: 43.648118, longitude: 79.392636 } + }, + { + key: "3", + type: "complaint", + location: "Bay St. | 0.8 mi.", + state: "open", + latlng: { latitude: 43.640168, longitude: -79.409373 } + }, + { + key: "4", + type: "work", + location: "Bloor St. | 1.2 mi.", + state: "open", + latlng: { latitude: 43.63311, longitude: -79.41588 } + }, + { + key: "5", + type: "inspection", + location: "Blue Jays Way | 2.2 mi.", + state: "open", + latlng: { latitude: 43.653526, longitude: -79.361385 } + }, + { + key: "6", + type: "complaint", + location: "Christie St. | 3.0 mi.", + state: "open", + latlng: { latitude: 43.66387, longitude: -79.383705 } + }, + { + key: "7", + type: "work", + location: "Cummer Ave. | 4.2 mi.", + state: "open", + latlng: { latitude: 43.659166, longitude: -79.39135 } + }, + { + key: "8", + type: "complaint", + location: "Danforth Ave. | 4.7 mi.", + state: "open", + latlng: { latitude: 43.663538, longitude: -79.423212 } + } ] const inspectionTypes = { work: { - title: 'Work Order' + title: "Work Order" }, inspection: { - title: 'Inspection', + title: "Inspection" }, complaint: { - title: 'Complaint', + title: "Complaint" } } export class WorkItemList extends React.Component { constructor(props) { super(props) + this.state = { + messageModal: null + } + api + .listWorkItems() + .then(list => {}) + .catch(() => { + this.setState({ + messageModal: { + icon: "hand", + message: "Unable to get list of work items", + detail: error.message + } + }) + }) } @autobind handleItemSelect(item, index) { - this.props.history.push('/activity') + this.props.history.push("/workitem") + } + + @autobind + handleItemDelete(item, index) { + } + + @autobind + handleMessageDismiss() { + this.setState({ messageModal: null }) } @autobind handleAddPress(item, index) { - this.props.history.push('/workitem') + this.props.history.push("/workitem") } @autobind @@ -57,31 +140,85 @@ export class WorkItemList extends React.Component { if (history.length > 1) { history.goBack() } else { - history.replace('/home') + history.replace("/home") } } render() { + const { messageModal } = this.state + return ( -
- + { - return ( - - {item.state.toUpperCase()} - - {inspectionTypes[item.type].title} - {item.location} + renderItem={({ item, index }) => ( + this.handleItemSelect(item, index)} + > + + + + {inspectionTypes[item.type].title} + + + {item.location} + - (this._handleItemSelect(item, index))} > - - + - ) - }} /> + + )} + renderHiddenItem={({ item, index }) => ( + this.handleItemDelete(item, index)} + > + + + Delete + + + + )} + rightOpenValue={-80} + stopLeftSwipe={100} + disableRightSwipe + /> + ) } diff --git a/website/src/Auth/Login.js b/website/src/Auth/Login.js index 339ba63..d026922 100644 --- a/website/src/Auth/Login.js +++ b/website/src/Auth/Login.js @@ -172,7 +172,6 @@ export class Login extends Component { -