diff --git a/Untitled b/Untitled
deleted file mode 100644
index e69de29..0000000
diff --git a/design/Deighton AR Design.sketch b/design/Deighton AR Design.sketch
index d174cc0..c5afdb8 100644
Binary files a/design/Deighton AR Design.sketch and b/design/Deighton AR Design.sketch differ
diff --git a/design/Deighton AR System.svg b/design/Deighton AR High Level.svg
similarity index 100%
rename from design/Deighton AR System.svg
rename to design/Deighton AR High Level.svg
diff --git a/design/Deighton AR System.docx b/design/Deighton AR Overview.docx
similarity index 100%
rename from design/Deighton AR System.docx
rename to design/Deighton AR Overview.docx
diff --git a/design/Deighton AR System.pdf b/design/Deighton AR System.pdf
new file mode 100644
index 0000000..dee8671
Binary files /dev/null and b/design/Deighton AR System.pdf differ
diff --git a/design/Deighton AR System.sketch b/design/Deighton AR System.sketch
new file mode 100644
index 0000000..39f2e9e
Binary files /dev/null and b/design/Deighton AR System.sketch differ
diff --git a/mobile/App.test.js b/mobile/App.test.js
deleted file mode 100644
index fc6f975..0000000
--- a/mobile/App.test.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import React from 'react';
-import App from './App';
-
-import renderer from 'react-test-renderer';
-
-it('renders without crashing', () => {
- const rendered = renderer.create().toJSON();
- expect(rendered).toBeTruthy();
-});
diff --git a/mobile/app.json b/mobile/app.json
index 8e85d20..29cc17c 100644
--- a/mobile/app.json
+++ b/mobile/app.json
@@ -1,7 +1,4 @@
{
- "expo": {
- "sdkVersion": "25.0.0"
- },
"name": "DeightonAR",
"displayName": "DeightonAR"
}
\ No newline at end of file
diff --git a/mobile/index.js b/mobile/index.js
index e9a0a0f..0ce8e0b 100644
--- a/mobile/index.js
+++ b/mobile/index.js
@@ -1,4 +1,4 @@
-import { AppRegistry } from 'react-native';
-import App from './src/App';
+import { AppRegistry } from 'react-native'
+import App from './src/App'
-AppRegistry.registerComponent('DeightonAR', () => App);
+AppRegistry.registerComponent('DeightonAR', () => App)
diff --git a/mobile/ios/DeightonAR.xcodeproj/project.pbxproj b/mobile/ios/DeightonAR.xcodeproj/project.pbxproj
index eb8c74e..ef92116 100644
--- a/mobile/ios/DeightonAR.xcodeproj/project.pbxproj
+++ b/mobile/ios/DeightonAR.xcodeproj/project.pbxproj
@@ -203,7 +203,6 @@
"${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework",
"${BUILT_PRODUCTS_DIR}/GoogleToolboxForMac/GoogleToolboxForMac.framework",
"${BUILT_PRODUCTS_DIR}/React/React.framework",
- "${BUILT_PRODUCTS_DIR}/ReactNativeNavigation/ReactNativeNavigation.framework",
"${PODS_ROOT}/../../node_modules/react-viro/ios/dist/ViroRenderer/ViroKit.framework",
"${BUILT_PRODUCTS_DIR}/react-native-maps/react_native_maps.framework",
"${BUILT_PRODUCTS_DIR}/yoga/yoga.framework",
@@ -218,7 +217,6 @@
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMSessionFetcher.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleToolboxForMac.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/React.framework",
- "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ReactNativeNavigation.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ViroKit.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/react_native_maps.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/yoga.framework",
diff --git a/mobile/ios/DeightonAR.xcodeproj/xcshareddata/xcschemes/DeightonAR.xcscheme b/mobile/ios/DeightonAR.xcodeproj/xcshareddata/xcschemes/DeightonAR.xcscheme
index 86578eb..8c1f88c 100644
--- a/mobile/ios/DeightonAR.xcodeproj/xcshareddata/xcschemes/DeightonAR.xcscheme
+++ b/mobile/ios/DeightonAR.xcodeproj/xcshareddata/xcschemes/DeightonAR.xcscheme
@@ -34,38 +34,15 @@
ReferencedContainer = "container:DeightonAR.xcodeproj">
-
-
-
-
-
-
-
-
#import
-// RNN: react-native-navigation
-#import "RCCManager.h"
-
// RNM: react-native-maps
// See https://developers.google.com/maps/documentation/ios-sdk/start
// and https://gist.github.com/dmachat/412658416dc7dcf3f7a829b7a10c2583
@@ -27,30 +24,25 @@
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
-/*
- VRTBundleURLProvider *bundleProvider = [[VRTBundleURLProvider alloc] init];
- jsCodeLocation = [bundleProvider jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
-
- if (jsCodeLocation == nil) {
- jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
- }
-*/
- // RNM: Uncomment for Google Maps support
- // [GMSServices provideAPIKey:@"AIzaSyDN4E_vzO4cKjKHkMg_49hX1GBnU34kx4U"];
-
NSURL *jsCodeLocation;
-
+
#ifdef DEBUG
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
#else
jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
- // Bootstrap RNN. See https://github.com/wix/react-native-navigation/blob/master/example/ios/example/AppDelegate.m
+ RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
+ moduleName:@"DeightonAR"
+ initialProperties:nil
+ launchOptions:launchOptions];
+ rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
+
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
- self.window.backgroundColor = [UIColor whiteColor];
- [[RCCManager sharedInstance] initBridgeWithBundleURL:jsCodeLocation launchOptions:launchOptions];
-
+ UIViewController *rootViewController = [UIViewController new];
+ rootViewController.view = rootView;
+ self.window.rootViewController = rootViewController;
+ [self.window makeKeyAndVisible];
return YES;
}
diff --git a/mobile/ios/Podfile b/mobile/ios/Podfile
index 35df62d..3d4f198 100644
--- a/mobile/ios/Podfile
+++ b/mobile/ios/Podfile
@@ -26,8 +26,6 @@ target 'DeightonAR' do
pod 'GLog', :podspec => '../node_modules/react-native/third-party-podspecs/GLog.podspec'
pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
- pod 'ReactNativeNavigation', :podspec => '../node_modules/react-native-navigation/ios/ReactNativeNavigation.podspec'
-
pod 'react-native-maps', path: '../node_modules/react-native-maps'
# pod 'react-native-google-maps', path: '../node_modules/react-native-maps'
# pod 'GoogleMaps'
diff --git a/mobile/ios/Podfile.lock b/mobile/ios/Podfile.lock
index 923f792..f2f4a8c 100644
--- a/mobile/ios/Podfile.lock
+++ b/mobile/ios/Podfile.lock
@@ -70,8 +70,6 @@ PODS:
- React/Core
- React/fishhook
- React/RCTBlob
- - ReactNativeNavigation (1.1.407):
- - React
- ViroKit (1.0):
- AWSDynamoDB (~> 2.6.7)
- GVRAudioSDK (= 1.120.0)
@@ -93,7 +91,6 @@ DEPENDENCIES:
- React/RCTNetwork (from `../node_modules/react-native`)
- React/RCTText (from `../node_modules/react-native`)
- React/RCTWebSocket (from `../node_modules/react-native`)
- - ReactNativeNavigation (from `../node_modules/react-native-navigation/ios/ReactNativeNavigation.podspec`)
- ViroKit (from `../node_modules/react-viro/ios/dist/ViroRenderer/`)
- ViroReact (from `../node_modules/react-viro/ios/`)
- yoga (from `../node_modules/react-native/ReactCommon/yoga`)
@@ -119,8 +116,6 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native"
react-native-maps:
:path: "../node_modules/react-native-maps"
- ReactNativeNavigation:
- :podspec: "../node_modules/react-native-navigation/ios/ReactNativeNavigation.podspec"
ViroKit:
:path: "../node_modules/react-viro/ios/dist/ViroRenderer/"
ViroReact:
@@ -141,11 +136,10 @@ SPEC CHECKSUMS:
GVRSDK: 0cb9d0ce06a84d698e61e3db9509766022dcf815
React: 541ba768b9855e10cdc76f55427a5cd0653ca806
react-native-maps: 066c2afcc89e18726377bcc685315f989ca22449
- ReactNativeNavigation: 0a0de59d82ed1accc9b762037752b11d794c7a4c
ViroKit: 9631f301ef6a3f56116b23d6aac5d5c2307aa368
ViroReact: 5520f26ac4654e361786c82da3b29ce0402c3c00
yoga: 17521bbb0dd54a47c0b3ac43253e78cdac7488e0
-PODFILE CHECKSUM: b13c8a6ef20af9f80e486813619c4b2833c402f8
+PODFILE CHECKSUM: 1de520fb7d323b83502ffda9c5bf993f7545cfa9
COCOAPODS: 1.5.0.beta.1
diff --git a/mobile/package-lock.json b/mobile/package-lock.json
index 6bea7fe..0c0ddba 100644
--- a/mobile/package-lock.json
+++ b/mobile/package-lock.json
@@ -5438,6 +5438,19 @@
}
}
},
+ "react-native-animatable": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/react-native-animatable/-/react-native-animatable-1.2.4.tgz",
+ "integrity": "sha512-cVTQXa/cp8gfxcl+l6I1rGAI7EeoNZ0ur9vtxb3tD5iGlJbIyUfQK61e6BycnZewdgQ639Mp6OrueXTpZlv76Q==",
+ "requires": {
+ "prop-types": "15.6.1"
+ }
+ },
+ "react-native-iphone-x-helper": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.0.2.tgz",
+ "integrity": "sha512-5FYNC4kTi/YK86l+r8GQ0xgsSL2tleCQ5Yppu1+ARbnm2qGRmDoJTGSNsWBAWa8FP1ORyhMjxi18IlvSRKaI2g=="
+ },
"react-native-maps": {
"version": "0.20.1",
"resolved": "https://registry.npmjs.org/react-native-maps/-/react-native-maps-0.20.1.tgz",
@@ -5493,6 +5506,15 @@
}
}
},
+ "react-native-modal": {
+ "version": "5.4.0",
+ "resolved": "https://registry.npmjs.org/react-native-modal/-/react-native-modal-5.4.0.tgz",
+ "integrity": "sha512-Bvq4FQPMAFijqjqNX6TxLgKOwdbruM6GvFwF9rb+mowbaFZVoYbHTKLaAbdPlrblgaZKWyOuuxBUoDx41+Xktg==",
+ "requires": {
+ "prop-types": "15.6.1",
+ "react-native-animatable": "1.2.4"
+ }
+ },
"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 3135294..c7c19c7 100644
--- a/mobile/package.json
+++ b/mobile/package.json
@@ -25,7 +25,9 @@
"react": "^16.2.0",
"react-form-binder": "^1.2.0",
"react-native": "^0.51.1",
+ "react-native-iphone-x-helper": "^1.0.2",
"react-native-maps": "^0.20.1",
+ "react-native-modal": "^5.4.0",
"react-router-native": "^4.2.0",
"react-viro": "^2.4.0",
"socket.io-client": "^2.0.4"
diff --git a/mobile/src/screens/Viewer.js b/mobile/src/ARViewer/ARViewer.js
similarity index 82%
rename from mobile/src/screens/Viewer.js
rename to mobile/src/ARViewer/ARViewer.js
index 5df97cd..651f212 100644
--- a/mobile/src/screens/Viewer.js
+++ b/mobile/src/ARViewer/ARViewer.js
@@ -4,6 +4,7 @@ import {
ViroARSceneNavigator, ViroARScene, ViroARPlane, ViroBox, ViroText, ViroAmbientLight
} from 'react-viro'
import autobind from 'autobind-decorator'
+import backImage from './images/back.png'
const styles = {
helloWorldTextStyle: {
@@ -31,7 +32,9 @@ class WorkItemSceneAR extends React.Component {
return (
{this.setState({text : "Hello World!"})}}>
-
@@ -41,11 +44,7 @@ class WorkItemSceneAR extends React.Component {
}
}
-export class Viewer extends React.Component {
- static navigatorStyle = {
- navBarHidden: true,
- }
-
+export class ARViewer extends React.Component {
constructor(props) {
super(props)
}
@@ -61,11 +60,11 @@ export class Viewer extends React.Component {
+ initialScene={{ scene: WorkItemSceneAR }} />
-
+
diff --git a/mobile/src/screens/images/back.png b/mobile/src/ARViewer/images/back.png
similarity index 100%
rename from mobile/src/screens/images/back.png
rename to mobile/src/ARViewer/images/back.png
diff --git a/mobile/src/ARViewer/index.js b/mobile/src/ARViewer/index.js
new file mode 100644
index 0000000..47c20c9
--- /dev/null
+++ b/mobile/src/ARViewer/index.js
@@ -0,0 +1 @@
+export { ARViewer } from './ARViewer'
\ No newline at end of file
diff --git a/mobile/src/screens/Activity.js b/mobile/src/Activity/Activity.js
similarity index 62%
rename from mobile/src/screens/Activity.js
rename to mobile/src/Activity/Activity.js
index 159ee39..b7cf767 100644
--- a/mobile/src/screens/Activity.js
+++ b/mobile/src/Activity/Activity.js
@@ -1,23 +1,8 @@
import React from 'react'
import { StyleSheet, View, TouchableOpacity, Image, ScrollView } from 'react-native'
-import backImage from './images/back.png'
+import autobind from 'autobind-decorator'
export class Activity extends React.Component {
- static navigatorButtons = {
- leftButtons: [
- {
- id: 'cancel',
- title: 'Cancel',
- }
- ],
- rightButtons: [
- {
- id: 'done',
- title: 'Done',
- }
- ]
- }
-
static styles = StyleSheet.create({
container: {
height: '100%',
@@ -28,20 +13,10 @@ export class Activity extends React.Component {
constructor(props) {
super(props)
- this.props.navigator.setOnNavigatorEvent(this._handleNavigatorEvent.bind(this))
}
- _handleNavigatorEvent(event) {
- switch (event.id) {
- case 'cancel':
- case 'done':
- this.props.navigator.pop()
- break
- }
- }
-
- _handlePressButton() {
- this.props.navigator.pop()
+ _handlePushButton(event) {
+ this.props.history.goBack()
}
render() {
diff --git a/mobile/src/Activity/index.js b/mobile/src/Activity/index.js
new file mode 100644
index 0000000..3d89983
--- /dev/null
+++ b/mobile/src/Activity/index.js
@@ -0,0 +1 @@
+export { Activity } from './Activity'
\ No newline at end of file
diff --git a/mobile/src/Auth/DefaultRoute.js b/mobile/src/Auth/DefaultRoute.js
new file mode 100644
index 0000000..38221a2
--- /dev/null
+++ b/mobile/src/Auth/DefaultRoute.js
@@ -0,0 +1,42 @@
+import React, { Fragment, Component } from 'react'
+import { api } from '../API'
+import { Route, Redirect } from 'react-router-native'
+import { View } from 'react-native'
+import autobind from 'autobind-decorator'
+
+export class DefaultRoute extends Component {
+ @autobind
+ updateComponent() {
+ this.forceUpdate()
+ }
+
+ componentDidMount() {
+ api.addListener('login', this.updateComponent)
+ }
+
+ componentWillUnmount() {
+ api.removeListener('login', this.updateComponent)
+ }
+
+ render() {
+ const user = api.loggedInUser
+ let path = null
+
+ if (user) {
+ if (!user.pending) {
+ path = '/home'
+ }
+ } else {
+ path = '/login'
+ }
+
+ const { location } = this.props
+
+ // Render a redirect or nothing until we finished logging on
+ return (
+ (
+ path ? : null
+ )} />
+ )
+ }
+}
diff --git a/mobile/src/screens/Login.js b/mobile/src/Auth/Login.js
similarity index 78%
rename from mobile/src/screens/Login.js
rename to mobile/src/Auth/Login.js
index 0e6a77c..4cfe074 100644
--- a/mobile/src/screens/Login.js
+++ b/mobile/src/Auth/Login.js
@@ -1,6 +1,7 @@
import React from 'react';
import { Platform, StyleSheet, Text, Image, Switch, TextInput,
- KeyboardAvoidingView, ScrollView, View, Button, Alert, InteractionManager } from 'react-native';
+ 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'
@@ -8,10 +9,6 @@ import { BoundSwitch, BoundInput, BoundButton } from '../ui'
import autobind from 'autobind-decorator'
export class Login extends React.Component {
- static navigatorStyle = {
- navBarHidden: true,
- }
-
static bindings = {
email: {
alwaysGet: true,
@@ -69,25 +66,37 @@ export class Login extends React.Component {
constructor(props) {
super(props)
this.state = {
- binder: new FormBinder({email: 'john@lyon-smith.org'}, Login.bindings)
+ binder: new FormBinder({email: 'john@lyon-smith.org'}, Login.bindings),
+ messageModal: null,
}
}
@autobind
handleLogin() {
let obj = this.state.binder.getModifiedFieldValues()
- let { navigator } = this.props
+ let { history } = this.props
if (obj) {
api.login(obj.email, obj.password, obj.rememberMe).then((user) => {
- this.props.navigator.dismissAllModals()
+ history.replace('/home')
}).catch((error) => {
- console.error(api.apiURL)
+ this.setState({ messageModal: {
+ icon: 'hand',
+ message: 'Unable to login',
+ detail: error.message,
+ }})
})
}
}
+ @autobind
+ handleMessageDismiss() {
+ this.setState({ messageModal: null })
+ }
+
render() {
+ const { messageModal } = this.state
+
return (
@@ -104,6 +113,12 @@ export class Login extends React.Component {
+
)
}
diff --git a/mobile/src/Auth/Logout.js b/mobile/src/Auth/Logout.js
new file mode 100644
index 0000000..6694437
--- /dev/null
+++ b/mobile/src/Auth/Logout.js
@@ -0,0 +1,21 @@
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
+import { api } from '../API'
+
+export class Logout extends Component {
+ static propTypes = {
+ history: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
+ }
+
+ componentDidMount(event) {
+ const cb = () => {
+ this.props.history.replace('/login')
+ }
+
+ api.logout().then(cb, cb)
+ }
+
+ render() {
+ return null
+ }
+}
diff --git a/mobile/src/Auth/ProtectedRoute.js b/mobile/src/Auth/ProtectedRoute.js
new file mode 100644
index 0000000..c3f9e51
--- /dev/null
+++ b/mobile/src/Auth/ProtectedRoute.js
@@ -0,0 +1,41 @@
+import React from 'react'
+import { Route, Redirect } from 'react-router-native'
+import { PropTypes } from 'prop-types'
+import { api } from '../API'
+import autobind from 'autobind-decorator'
+
+export class ProtectedRoute extends React.Component {
+ static propTypes = {
+ location: PropTypes.shape({ pathname: PropTypes.string, search: PropTypes.string }),
+ admin: PropTypes.bool,
+ }
+
+ @autobind
+ updateComponent() {
+ this.forceUpdate()
+ }
+
+ componentDidMount() {
+ api.addListener('login', this.updateComponent)
+ }
+
+ componentWillUnmount() {
+ api.removeListener('login', this.updateComponent)
+ }
+
+ render(props) {
+ const user = api.loggedInUser
+
+ if (user) {
+ if (user.pending) {
+ // The API might be in the middle of fetching the user information
+ return null
+ } else if (!this.props.admin || (this.props.admin && user.administrator)) {
+ return
+ }
+ }
+
+ // TODO: Can add redirect back in here - see website
+ return
+ }
+}
diff --git a/mobile/src/screens/images/deighton.png b/mobile/src/Auth/images/deighton.png
similarity index 100%
rename from mobile/src/screens/images/deighton.png
rename to mobile/src/Auth/images/deighton.png
diff --git a/mobile/src/Auth/index.js b/mobile/src/Auth/index.js
new file mode 100644
index 0000000..5437cba
--- /dev/null
+++ b/mobile/src/Auth/index.js
@@ -0,0 +1,4 @@
+export { Login } from './Login'
+export { Logout } from './Logout'
+export { ProtectedRoute } from './ProtectedRoute'
+export { DefaultRoute } from './DefaultRoute'
diff --git a/mobile/src/Home/Home.js b/mobile/src/Home/Home.js
new file mode 100644
index 0000000..42e27bd
--- /dev/null
+++ b/mobile/src/Home/Home.js
@@ -0,0 +1,131 @@
+import React from 'react'
+import { StyleSheet, Text, TextInput, FlatList, Image, View, TouchableOpacity } from 'react-native'
+import MapView, { Marker } from 'react-native-maps'
+import { Icon, Header } from '../ui'
+import { api } from '../API'
+import autobind from 'autobind-decorator'
+import pinImage from './images/pin.png'
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: '#FFFFFF',
+ alignItems: 'flex-start',
+ justifyContent: 'flex-start',
+ }
+})
+
+const data = [
+ {key: '1', title: 'Remove Animal Carcass', location: 'Ossington Ave. | 0.2 mi.', state: 'planned', latlng: { latitude: 43.653226, longitude: -79.383184 } },
+ {key: '2', title: 'Fix sign post', location: 'Alexandre St. | 0.7 mi.', state: 'open', latlng: { latitude: 43.648118, longitude: 79.392636 }},
+ {key: '3', title: 'Overflowing trash', location: 'Bay St. | 0.8 mi.', state: 'open', latlng: { latitude: 43.640168, longitude: -79.409373 }},
+ {key: '4', title: 'Leaking water pipe', location: 'Bloor St. | 1.2 mi.', state: 'planned', latlng: { latitude: 43.633110, longitude: -79.415880 }},
+ {key: '5', title: 'Tree branch in road', location: 'Blue Jays Way | 2.2 mi.', state: 'open', latlng: { latitude: 43.653526, longitude: -79.361385 }},
+ {key: '6', title: 'Washing machine on sidewalk', location: 'Christie St. | 3.0 mi.', state: 'open', latlng: { latitude: 43.663870, longitude: -79.383705 }},
+ {key: '7', title: 'Dead moose', location: 'Cummer Ave. | 4.2 mi.', state: 'open', latlng: { latitude: 43.659166, longitude: -79.391350 }},
+ {key: '8', title: 'Glass in street', location: 'Danforth Ave. | 4.7 mi.', state: 'open', latlng: { latitude: 43.663538, longitude: -79.423212 }},
+]
+
+export class Home extends React.Component {
+ constructor(props) {
+ super(props)
+ }
+
+ @autobind
+ _handleNavigatorEvent(event) {
+ switch (event.id) {
+ case 'logout':
+ api.logout().then(() => {
+ this.props.history.replace('/login')
+ })
+ break
+ case 'viewer':
+ this.props.push('/viewer')
+ break
+ }
+ }
+
+ @autobind
+ handleAdminButton() {
+ this.props.history.replace('/admin')
+ }
+
+ @autobind
+ handleItemSelect(item, index) {
+ this.props.history.replace('/activity')
+ }
+
+ @autobind
+ handleLogout() {
+ this.props.history.replace('/logout')
+ }
+
+ render() {
+ return (
+
+
+
+ {
+ data.map(marker => (
+
+ ))
+ }
+
+
+
+
+
+
+ {
+ return (
+
+ {item.state.toUpperCase()}
+
+ {item.title}
+ {item.location}
+
+ (this._handleItemSelect(item, index))} >
+
+
+
+ )
+ }} />
+
+
+
+
+ Hide List
+
+
+
+
+
+ );
+ }
+}
diff --git a/mobile/src/screens/images/pin.png b/mobile/src/Home/images/pin.png
similarity index 100%
rename from mobile/src/screens/images/pin.png
rename to mobile/src/Home/images/pin.png
diff --git a/mobile/src/Home/index.js b/mobile/src/Home/index.js
new file mode 100644
index 0000000..8f0854d
--- /dev/null
+++ b/mobile/src/Home/index.js
@@ -0,0 +1 @@
+export { Home } from './Home'
\ No newline at end of file
diff --git a/mobile/src/Modal/MessageModal.js b/mobile/src/Modal/MessageModal.js
new file mode 100644
index 0000000..1225f29
--- /dev/null
+++ b/mobile/src/Modal/MessageModal.js
@@ -0,0 +1,62 @@
+import React, { Component } from 'react'
+import Modal from 'react-native-modal'
+import PropTypes from 'prop-types'
+import { View, Text, TouchableOpacity } from 'react-native'
+import { Icon } from '../ui'
+import autobind from 'autobind-decorator'
+
+export class MessageModal extends Component {
+ static propTypes = {
+ open: PropTypes.bool,
+ icon: PropTypes.string.isRequired,
+ message: PropTypes.string.isRequired,
+ detail: PropTypes.string,
+ onDismiss: PropTypes.func
+ }
+
+ @autobind
+ handleButtonPress() {
+ const { onDismiss } = this.props
+
+ if (onDismiss) {
+ onDismiss()
+ }
+ }
+
+ constructor(props) {
+ super(props)
+ this.state = {
+
+ }
+ }
+
+ render() {
+ const { open, icon, message, detail } = this.props
+
+ return (
+
+
+
+
+ {message}
+ {detail}
+
+ OK
+
+
+
+
+ )
+ }
+}
\ No newline at end of file
diff --git a/mobile/src/Modal/index.js b/mobile/src/Modal/index.js
new file mode 100644
index 0000000..7852cbf
--- /dev/null
+++ b/mobile/src/Modal/index.js
@@ -0,0 +1 @@
+export { MessageModal } from './MessageModal'
\ No newline at end of file
diff --git a/mobile/src/screens/WorkItem.js b/mobile/src/WorkItem/WorkItem.js
similarity index 70%
rename from mobile/src/screens/WorkItem.js
rename to mobile/src/WorkItem/WorkItem.js
index 528c6bc..fd09f6e 100644
--- a/mobile/src/screens/WorkItem.js
+++ b/mobile/src/WorkItem/WorkItem.js
@@ -2,21 +2,6 @@ import React from 'react'
import { StyleSheet, View, TouchableOpacity, Image, ScrollView, Picker, Text } from 'react-native'
export class WorkItem extends React.Component {
- static navigatorButtons = {
- leftButtons: [
- {
- id: 'cancel',
- title: 'Cancel',
- }
- ],
- rightButtons: [
- {
- id: 'done',
- title: 'Done',
- }
- ]
- }
-
static styles = StyleSheet.create({
container: {
height: '100%',
@@ -27,20 +12,6 @@ export class WorkItem extends React.Component {
constructor(props) {
super(props)
- this.props.navigator.setOnNavigatorEvent(this._handleNavigatorEvent.bind(this))
- }
-
- _handleNavigatorEvent(event) {
- switch (event.id) {
- case 'cancel':
- case 'done':
- this.props.navigator.pop()
- break
- }
- }
-
- _handlePressButton() {
- this.props.navigator.pop()
}
render() {
diff --git a/mobile/src/WorkItem/WorkItemList.js b/mobile/src/WorkItem/WorkItemList.js
new file mode 100644
index 0000000..4980bd9
--- /dev/null
+++ b/mobile/src/WorkItem/WorkItemList.js
@@ -0,0 +1,71 @@
+import React from 'react'
+import { StyleSheet, View, TouchableOpacity, Image, FlatList, Text} from 'react-native'
+import autobind from 'autobind-decorator'
+
+const styles = StyleSheet.create({
+ container: {
+ 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 }},
+]
+
+const inspectionTypes = {
+ work: {
+ title: 'Work Order'
+ },
+ inspection: {
+ title: 'Inspection',
+ },
+ complaint: {
+ title: 'Complaint',
+ }
+}
+
+export class WorkItemList extends React.Component {
+ constructor(props) {
+ super(props)
+ this._handleItemSelect = this._handleItemSelect.bind(this)
+ }
+
+ @autobind
+ _handleItemSelect(item, index) {
+ this.props.history.push('/activity')
+ }
+
+ render() {
+ return (
+
+ {
+ return (
+
+ {item.state.toUpperCase()}
+
+ {Admin.inspectionTypes[item.type].title}
+ {item.location}
+
+ (this._handleItemSelect(item, index))} >
+
+
+
+ )
+ }} />
+
+ );
+ }
+}
diff --git a/mobile/src/WorkItem/index.js b/mobile/src/WorkItem/index.js
new file mode 100644
index 0000000..a3f6b2c
--- /dev/null
+++ b/mobile/src/WorkItem/index.js
@@ -0,0 +1,3 @@
+export { WorkItem } from './WorkItem'
+export { WorkItemList } from './WorkItemList'
+
diff --git a/mobile/src/app.js b/mobile/src/app.js
index 18c312b..244baf4 100644
--- a/mobile/src/app.js
+++ b/mobile/src/app.js
@@ -1,11 +1,15 @@
import React from 'react'
-import { View, StyleSheet, Text, TouchableHighlight, Image } from 'react-native'
+import { View, StyleSheet } from 'react-native'
import {
ViroARSceneNavigator, ViroARScene, ViroARPlane, ViroBox
} from 'react-viro'
-import { NativeRouter, Route, Link } from 'react-router-native'
+import { NativeRouter, Route, Link, Switch } from 'react-router-native'
import MapView from 'react-native-maps'
-import { Home, Viewer, WorkItem, Admin, Login, Activity } from './screens'
+import { WorkItem, WorkItemList } from './WorkItem'
+import { Activity } from './Activity'
+import { Home } from './Home'
+import { ARViewer } from './ARViewer'
+import { Login, Logout, ProtectedRoute, DefaultRoute } from './Auth'
// See https://github.com/facebook/react-native/issues/12981
console.ignoredYellowBox = [
@@ -16,13 +20,17 @@ export default class App extends React.Component {
render() {
return (
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
)
diff --git a/mobile/src/screens/Admin.js b/mobile/src/screens/Admin.js
deleted file mode 100644
index 0576614..0000000
--- a/mobile/src/screens/Admin.js
+++ /dev/null
@@ -1,93 +0,0 @@
-import React from 'react'
-import { StyleSheet, View, TouchableOpacity, Image, FlatList, Text} from 'react-native'
-import backImage from './images/back.png'
-import rightArrowImage from './images/right-arrow.png'
-
-export class Admin extends React.Component {
- static navigatorButtons = {
- leftButtons: [
- {
- id: 'back',
- icon: require('./images/back.png'),
- }
- ]
- }
-
- static 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 }},
- ]
-
- static inspectionTypes = {
- work: {
- title: 'Work Order'
- },
- inspection: {
- title: 'Inspection',
- },
- complaint: {
- title: 'Complaint',
- }
- }
-
- static styles = StyleSheet.create({
- container: {
- height: '100%',
- width: '100%',
- justifyContent: 'flex-start',
- backgroundColor: '#FFFFFF',
- },
- })
-
- constructor(props) {
- super(props)
- this.props.navigator.setOnNavigatorEvent(this._handleNavigatorEvent.bind(this))
- this._handleItemSelect = this._handleItemSelect.bind(this)
- }
-
- _handleNavigatorEvent(event) {
- switch (event.id) {
- case 'back':
- this.props.navigator.pop()
- break
- }
- }
-
- _handleItemSelect(item, index) {
- this.props.navigator.push({
- screen: 'app.WorkItem',
- title: 'Work Item',
- passProps: { item, index },
- })
- }
-
- render() {
- return (
-
- {
- return (
-
- {item.state.toUpperCase()}
-
- {Admin.inspectionTypes[item.type].title}
- {item.location}
-
- (this._handleItemSelect(item, index))} >
-
-
-
- )
- }} />
-
- );
- }
-}
diff --git a/mobile/src/screens/Error.js b/mobile/src/screens/Error.js
deleted file mode 100644
index a068362..0000000
--- a/mobile/src/screens/Error.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import React from 'react';
-import { StyleSheet, Text, Image, Button } from 'react-native';
-
-export class Error extends React.Component {
- static navigatorStyle = {
- navBarHidden: true,
- }
-
- static styles = StyleSheet.create({
- page: {
- minHeight: '50%',
- flex: 1,
- backgroundColor: '#fff',
- alignItems: 'center',
- justifyContent: 'center',
- },
- })
-
- render() {
- return (
-
-
- )
- }
-}
diff --git a/mobile/src/screens/Home.js b/mobile/src/screens/Home.js
deleted file mode 100644
index c8a2937..0000000
--- a/mobile/src/screens/Home.js
+++ /dev/null
@@ -1,166 +0,0 @@
-import React from 'react'
-import { StyleSheet, Text, TextInput, FlatList, Image, View, TouchableOpacity } from 'react-native'
-import MapView, { Marker } from 'react-native-maps'
-import { api } from '../API'
-import centerImage from './images/center.png'
-import settingsImage from './images/settings.png'
-import searchImage from './images/search.png'
-import cancelImage from './images/cancel.png'
-import pinImage from './images/pin.png'
-import rightArrowImage from './images/right-arrow.png'
-
-export class Home extends React.Component {
- static navigatorButtons = {
- rightButtons: [
- {
- id: 'viewer',
- icon: require('./images/ar-glases.png'),
- }
- ],
- leftButtons: [
- {
- id: 'logout',
- icon: require('./images/logout.png'),
- }
- ]
- }
-
- static styles = StyleSheet.create({
- container: {
- flex: 1,
- backgroundColor: '#fff',
- alignItems: 'flex-start',
- justifyContent: 'flex-start',
- }
- })
-
- static data = [
- {key: '1', title: 'Remove Animal Carcass', location: 'Ossington Ave. | 0.2 mi.', state: 'planned', latlng: { latitude: 43.653226, longitude: -79.383184 } },
- {key: '2', title: 'Fix sign post', location: 'Alexandre St. | 0.7 mi.', state: 'open', latlng: { latitude: 43.648118, longitude: 79.392636 }},
- {key: '3', title: 'Overflowing trash', location: 'Bay St. | 0.8 mi.', state: 'open', latlng: { latitude: 43.640168, longitude: -79.409373 }},
- {key: '4', title: 'Leaking water pipe', location: 'Bloor St. | 1.2 mi.', state: 'planned', latlng: { latitude: 43.633110, longitude: -79.415880 }},
- {key: '5', title: 'Tree branch in road', location: 'Blue Jays Way | 2.2 mi.', state: 'open', latlng: { latitude: 43.653526, longitude: -79.361385 }},
- {key: '6', title: 'Washing machine on sidewalk', location: 'Christie St. | 3.0 mi.', state: 'open', latlng: { latitude: 43.663870, longitude: -79.383705 }},
- {key: '7', title: 'Dead moose', location: 'Cummer Ave. | 4.2 mi.', state: 'open', latlng: { latitude: 43.659166, longitude: -79.391350 }},
- {key: '8', title: 'Glass in street', location: 'Danforth Ave. | 4.7 mi.', state: 'open', latlng: { latitude: 43.663538, longitude: -79.423212 }},
- ]
-
- constructor(props) {
- super(props);
- this.props.navigator.setOnNavigatorEvent(this._handleNavigatorEvent.bind(this))
- this._handleAdminButton = this._handleAdminButton.bind(this)
- this._handleItemSelect = this._handleItemSelect.bind(this)
- }
-
- _handleNavigatorEvent(event) {
- switch (event.id) {
- case 'logout':
- api.logout().then(() => {
- this.props.navigator.showModal({ screen: 'app.Login' })
- })
- break
- case 'viewer':
- this.props.navigator.push({
- screen: 'app.Viewer',
- animation: 'slide-horizontal',
- })
- break
- case 'willAppear':
- break
- case 'didAppear':
- if (!api.loggedInUser) {
- this.props.navigator.showModal({ screen: 'app.Login' })
- }
- break
- case 'willDisappear':
- break
- case 'didDisappear':
- break
- case 'willCommitPreview':
- break
- }
- }
-
- _handleAdminButton() {
- this.props.navigator.push({
- screen: 'app.Admin',
- title: 'Work Items',
- })
- }
-
- _handleItemSelect(item, index) {
- this.props.navigator.push({
- screen: 'app.Activity',
- title: 'Activity',
- passProps: { item, index },
- })
- }
-
- render() {
- return (
-
-
- {
- Home.data.map(marker => (
-
- ))
- }
-
-
-
-
-
-
- {
- return (
-
- {item.state.toUpperCase()}
-
- {item.title}
- {item.location}
-
- (this._handleItemSelect(item, index))} >
-
-
-
- )
- }} />
-
-
-
-
- Hide List
-
-
-
-
-
- );
- }
-}
diff --git a/mobile/src/screens/images/back_arrow.png b/mobile/src/screens/images/back_arrow.png
deleted file mode 100644
index 5b64c34..0000000
Binary files a/mobile/src/screens/images/back_arrow.png and /dev/null differ
diff --git a/mobile/src/screens/index.js b/mobile/src/screens/index.js
deleted file mode 100644
index 0a2e100..0000000
--- a/mobile/src/screens/index.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { Home } from './Home'
-import { Login } from './Login'
-import { Error } from './Error'
-import { Viewer } from './Viewer'
-import { Activity } from './Activity'
-import { WorkItem } from './WorkItem'
-import { Admin } from './Admin'
-import { Navigation } from 'react-native-navigation'
-
-export function registerScreens() {
- Navigation.registerComponent('app.Home', () => Home)
- Navigation.registerComponent('app.Login', () => Login)
- Navigation.registerComponent('app.Viewer', () => Viewer)
- Navigation.registerComponent('app.Activity', () => Activity)
- Navigation.registerComponent('app.Admin', () => Admin)
- Navigation.registerComponent('app.WorkItem', () => WorkItem)
- Navigation.registerComponent('app.Error', () => Error)
-}
diff --git a/mobile/src/ui/Header.js b/mobile/src/ui/Header.js
new file mode 100644
index 0000000..5d84c0d
--- /dev/null
+++ b/mobile/src/ui/Header.js
@@ -0,0 +1,39 @@
+import React, { Component } from 'react'
+import { View, TouchableOpacity, Text } from 'react-native'
+import { Icon } from './Icon'
+import PropTypes from 'prop-types'
+import { ifIphoneX } from 'react-native-iphone-x-helper'
+
+const headerButtonShape = { icon: PropTypes.string.isRequired, onPress: PropTypes.func }
+
+export class Header extends Component {
+ static propTypes = {
+ title: PropTypes.string.isRequired,
+ leftButton: PropTypes.shape(headerButtonShape),
+ rightButton: PropTypes.shape(headerButtonShape),
+ }
+
+ render() {
+ const { title, leftButton, rightButton } = this.props
+
+ return (
+
+
+
+
+ {title}
+
+
+
+
+ )
+ }
+}
diff --git a/mobile/src/ui/Icon.js b/mobile/src/ui/Icon.js
new file mode 100644
index 0000000..b2022e2
--- /dev/null
+++ b/mobile/src/ui/Icon.js
@@ -0,0 +1,37 @@
+import React, { Component } from 'react'
+import { Image } from 'react-native'
+import PropTypes from 'prop-types'
+
+const images = {
+ logout: require('./images/logout.png'),
+ glasses: require('./images/ar-glasses.png'),
+ back: require('./images/back.png'),
+ hand: require('./images/hand.png'),
+ center: require('./images/center.png'),
+ rightArrow: require('./images/right-arrow.png'),
+ search: require('./images/search.png'),
+ settings: require('./images/settings.png'),
+}
+
+export class Icon extends Component {
+ static propTypes = {
+ name: PropTypes.string.isRequired,
+ size: PropTypes.number,
+ margin: PropTypes.number,
+ style: PropTypes.object,
+ }
+
+ static defaultProps = {
+ size: 50,
+ margin: 0,
+ }
+
+ render() {
+ let { size, name, margin, style } = this.props
+ let source = images[name] || images['hand']
+
+ size -= margin * 2
+
+ return
+ }
+}
diff --git a/mobile/src/screens/images/ar-glases.png b/mobile/src/ui/images/ar-glasses.png
similarity index 100%
rename from mobile/src/screens/images/ar-glases.png
rename to mobile/src/ui/images/ar-glasses.png
diff --git a/mobile/src/ui/images/back.png b/mobile/src/ui/images/back.png
new file mode 100644
index 0000000..40b09e4
Binary files /dev/null and b/mobile/src/ui/images/back.png differ
diff --git a/mobile/src/screens/images/cancel.png b/mobile/src/ui/images/cancel.png
similarity index 100%
rename from mobile/src/screens/images/cancel.png
rename to mobile/src/ui/images/cancel.png
diff --git a/mobile/src/screens/images/center.png b/mobile/src/ui/images/center.png
similarity index 100%
rename from mobile/src/screens/images/center.png
rename to mobile/src/ui/images/center.png
diff --git a/mobile/src/ui/images/hand.png b/mobile/src/ui/images/hand.png
new file mode 100644
index 0000000..aeb51f6
Binary files /dev/null and b/mobile/src/ui/images/hand.png differ
diff --git a/mobile/src/screens/images/logout.png b/mobile/src/ui/images/logout.png
similarity index 100%
rename from mobile/src/screens/images/logout.png
rename to mobile/src/ui/images/logout.png
diff --git a/mobile/src/screens/images/right-arrow.png b/mobile/src/ui/images/right-arrow.png
similarity index 100%
rename from mobile/src/screens/images/right-arrow.png
rename to mobile/src/ui/images/right-arrow.png
diff --git a/mobile/src/screens/images/search.png b/mobile/src/ui/images/search.png
similarity index 100%
rename from mobile/src/screens/images/search.png
rename to mobile/src/ui/images/search.png
diff --git a/mobile/src/screens/images/settings.png b/mobile/src/ui/images/settings.png
similarity index 100%
rename from mobile/src/screens/images/settings.png
rename to mobile/src/ui/images/settings.png
diff --git a/mobile/src/ui/index.js b/mobile/src/ui/index.js
index 1e79c76..ca42c78 100644
--- a/mobile/src/ui/index.js
+++ b/mobile/src/ui/index.js
@@ -1,3 +1,5 @@
export { BoundSwitch } from './BoundSwitch'
export { BoundInput } from './BoundInput'
export { BoundButton } from './BoundButton'
+export { Icon } from './Icon'
+export { Header } from './Header'
diff --git a/server/src/api/routes/AuthRoutes.js b/server/src/api/routes/AuthRoutes.js
index 2fcd8a0..7d06e68 100644
--- a/server/src/api/routes/AuthRoutes.js
+++ b/server/src/api/routes/AuthRoutes.js
@@ -77,7 +77,7 @@ export class AuthRoutes {
}
let cr = credential()
- const isValid = cr.verify(JSON.stringify(user.passwordHash), req.body.password)
+ const isValid = await cr.verify(JSON.stringify(user.passwordHash), req.body.password)
if (isValid) {
user.loginToken = loginToken.pack(user._id.toString(), user.email)
diff --git a/website/src/Auth/DefaultRoute.js b/website/src/Auth/DefaultRoute.js
index 4612f70..6886e36 100644
--- a/website/src/Auth/DefaultRoute.js
+++ b/website/src/Auth/DefaultRoute.js
@@ -20,14 +20,14 @@ export class DefaultRoute extends Component {
render() {
const user = api.loggedInUser
- let redirect = null
+ let path = null
if (user) {
if (!user.pending) {
- redirect =
+ path = user.administrator ? '/home' : '/profile'
}
} else {
- redirect =
+ path = '/login'
}
return (
@@ -37,7 +37,7 @@ export class DefaultRoute extends Component {
return (
- {redirect}
+ {path ? : null}
)
}}
diff --git a/website/src/Auth/ProtectedRoute.js b/website/src/Auth/ProtectedRoute.js
index 11d9a52..6e8eb5f 100644
--- a/website/src/Auth/ProtectedRoute.js
+++ b/website/src/Auth/ProtectedRoute.js
@@ -1,5 +1,5 @@
import React from 'react'
-import { Route, Redirect } from 'react-router-dom'
+import { Route, Redirect } from 'react-router'
import { PropTypes } from 'prop-types'
import { api } from 'src/API'
import autobind from 'autobind-decorator'