Added Header, Icon and MessageModal. Refactor screens into directories.
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
BIN
design/Deighton AR System.pdf
Normal file
BIN
design/Deighton AR System.sketch
Normal file
@@ -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(<App />).toJSON();
|
|
||||||
expect(rendered).toBeTruthy();
|
|
||||||
});
|
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
{
|
{
|
||||||
"expo": {
|
|
||||||
"sdkVersion": "25.0.0"
|
|
||||||
},
|
|
||||||
"name": "DeightonAR",
|
"name": "DeightonAR",
|
||||||
"displayName": "DeightonAR"
|
"displayName": "DeightonAR"
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { AppRegistry } from 'react-native';
|
import { AppRegistry } from 'react-native'
|
||||||
import App from './src/App';
|
import App from './src/App'
|
||||||
|
|
||||||
AppRegistry.registerComponent('DeightonAR', () => App);
|
AppRegistry.registerComponent('DeightonAR', () => App)
|
||||||
|
|||||||
@@ -203,7 +203,6 @@
|
|||||||
"${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework",
|
"${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/GoogleToolboxForMac/GoogleToolboxForMac.framework",
|
"${BUILT_PRODUCTS_DIR}/GoogleToolboxForMac/GoogleToolboxForMac.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/React/React.framework",
|
"${BUILT_PRODUCTS_DIR}/React/React.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/ReactNativeNavigation/ReactNativeNavigation.framework",
|
|
||||||
"${PODS_ROOT}/../../node_modules/react-viro/ios/dist/ViroRenderer/ViroKit.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}/react-native-maps/react_native_maps.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/yoga/yoga.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}/GTMSessionFetcher.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleToolboxForMac.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleToolboxForMac.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/React.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}/ViroKit.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/react_native_maps.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/react_native_maps.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/yoga.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/yoga.framework",
|
||||||
|
|||||||
@@ -34,38 +34,15 @@
|
|||||||
ReferencedContainer = "container:DeightonAR.xcodeproj">
|
ReferencedContainer = "container:DeightonAR.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildActionEntry>
|
</BuildActionEntry>
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "NO"
|
|
||||||
buildForArchiving = "NO"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "00E356ED1AD99517003FC87E"
|
|
||||||
BuildableName = "DeightonARTests.xctest"
|
|
||||||
BlueprintName = "DeightonARTests"
|
|
||||||
ReferencedContainer = "container:DeightonAR.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
</BuildActionEntries>
|
</BuildActionEntries>
|
||||||
</BuildAction>
|
</BuildAction>
|
||||||
<TestAction
|
<TestAction
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
language = ""
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
<Testables>
|
<Testables>
|
||||||
<TestableReference
|
|
||||||
skipped = "NO">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "00E356ED1AD99517003FC87E"
|
|
||||||
BuildableName = "DeightonARTests.xctest"
|
|
||||||
BlueprintName = "DeightonARTests"
|
|
||||||
ReferencedContainer = "container:DeightonAR.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</TestableReference>
|
|
||||||
</Testables>
|
</Testables>
|
||||||
<MacroExpansion>
|
<MacroExpansion>
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
@@ -83,6 +60,7 @@
|
|||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
language = ""
|
||||||
launchStyle = "0"
|
launchStyle = "0"
|
||||||
useCustomWorkingDirectory = "NO"
|
useCustomWorkingDirectory = "NO"
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
|||||||
@@ -12,9 +12,6 @@
|
|||||||
#import <React/RCTBundleURLProvider.h>
|
#import <React/RCTBundleURLProvider.h>
|
||||||
#import <React/RCTRootView.h>
|
#import <React/RCTRootView.h>
|
||||||
|
|
||||||
// RNN: react-native-navigation
|
|
||||||
#import "RCCManager.h"
|
|
||||||
|
|
||||||
// RNM: react-native-maps
|
// RNM: react-native-maps
|
||||||
// See https://developers.google.com/maps/documentation/ios-sdk/start
|
// See https://developers.google.com/maps/documentation/ios-sdk/start
|
||||||
// and https://gist.github.com/dmachat/412658416dc7dcf3f7a829b7a10c2583
|
// and https://gist.github.com/dmachat/412658416dc7dcf3f7a829b7a10c2583
|
||||||
@@ -27,30 +24,25 @@
|
|||||||
|
|
||||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
- (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;
|
NSURL *jsCodeLocation;
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
|
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
|
||||||
#else
|
#else
|
||||||
jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
|
jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
|
||||||
#endif
|
#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 = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||||
self.window.backgroundColor = [UIColor whiteColor];
|
UIViewController *rootViewController = [UIViewController new];
|
||||||
[[RCCManager sharedInstance] initBridgeWithBundleURL:jsCodeLocation launchOptions:launchOptions];
|
rootViewController.view = rootView;
|
||||||
|
self.window.rootViewController = rootViewController;
|
||||||
|
[self.window makeKeyAndVisible];
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,8 +26,6 @@ target 'DeightonAR' do
|
|||||||
pod 'GLog', :podspec => '../node_modules/react-native/third-party-podspecs/GLog.podspec'
|
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 '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-maps', path: '../node_modules/react-native-maps'
|
||||||
# pod 'react-native-google-maps', path: '../node_modules/react-native-maps'
|
# pod 'react-native-google-maps', path: '../node_modules/react-native-maps'
|
||||||
# pod 'GoogleMaps'
|
# pod 'GoogleMaps'
|
||||||
|
|||||||
@@ -70,8 +70,6 @@ PODS:
|
|||||||
- React/Core
|
- React/Core
|
||||||
- React/fishhook
|
- React/fishhook
|
||||||
- React/RCTBlob
|
- React/RCTBlob
|
||||||
- ReactNativeNavigation (1.1.407):
|
|
||||||
- React
|
|
||||||
- ViroKit (1.0):
|
- ViroKit (1.0):
|
||||||
- AWSDynamoDB (~> 2.6.7)
|
- AWSDynamoDB (~> 2.6.7)
|
||||||
- GVRAudioSDK (= 1.120.0)
|
- GVRAudioSDK (= 1.120.0)
|
||||||
@@ -93,7 +91,6 @@ DEPENDENCIES:
|
|||||||
- React/RCTNetwork (from `../node_modules/react-native`)
|
- React/RCTNetwork (from `../node_modules/react-native`)
|
||||||
- React/RCTText (from `../node_modules/react-native`)
|
- React/RCTText (from `../node_modules/react-native`)
|
||||||
- React/RCTWebSocket (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/`)
|
- ViroKit (from `../node_modules/react-viro/ios/dist/ViroRenderer/`)
|
||||||
- ViroReact (from `../node_modules/react-viro/ios/`)
|
- ViroReact (from `../node_modules/react-viro/ios/`)
|
||||||
- yoga (from `../node_modules/react-native/ReactCommon/yoga`)
|
- yoga (from `../node_modules/react-native/ReactCommon/yoga`)
|
||||||
@@ -119,8 +116,6 @@ EXTERNAL SOURCES:
|
|||||||
:path: "../node_modules/react-native"
|
:path: "../node_modules/react-native"
|
||||||
react-native-maps:
|
react-native-maps:
|
||||||
:path: "../node_modules/react-native-maps"
|
:path: "../node_modules/react-native-maps"
|
||||||
ReactNativeNavigation:
|
|
||||||
:podspec: "../node_modules/react-native-navigation/ios/ReactNativeNavigation.podspec"
|
|
||||||
ViroKit:
|
ViroKit:
|
||||||
:path: "../node_modules/react-viro/ios/dist/ViroRenderer/"
|
:path: "../node_modules/react-viro/ios/dist/ViroRenderer/"
|
||||||
ViroReact:
|
ViroReact:
|
||||||
@@ -141,11 +136,10 @@ SPEC CHECKSUMS:
|
|||||||
GVRSDK: 0cb9d0ce06a84d698e61e3db9509766022dcf815
|
GVRSDK: 0cb9d0ce06a84d698e61e3db9509766022dcf815
|
||||||
React: 541ba768b9855e10cdc76f55427a5cd0653ca806
|
React: 541ba768b9855e10cdc76f55427a5cd0653ca806
|
||||||
react-native-maps: 066c2afcc89e18726377bcc685315f989ca22449
|
react-native-maps: 066c2afcc89e18726377bcc685315f989ca22449
|
||||||
ReactNativeNavigation: 0a0de59d82ed1accc9b762037752b11d794c7a4c
|
|
||||||
ViroKit: 9631f301ef6a3f56116b23d6aac5d5c2307aa368
|
ViroKit: 9631f301ef6a3f56116b23d6aac5d5c2307aa368
|
||||||
ViroReact: 5520f26ac4654e361786c82da3b29ce0402c3c00
|
ViroReact: 5520f26ac4654e361786c82da3b29ce0402c3c00
|
||||||
yoga: 17521bbb0dd54a47c0b3ac43253e78cdac7488e0
|
yoga: 17521bbb0dd54a47c0b3ac43253e78cdac7488e0
|
||||||
|
|
||||||
PODFILE CHECKSUM: b13c8a6ef20af9f80e486813619c4b2833c402f8
|
PODFILE CHECKSUM: 1de520fb7d323b83502ffda9c5bf993f7545cfa9
|
||||||
|
|
||||||
COCOAPODS: 1.5.0.beta.1
|
COCOAPODS: 1.5.0.beta.1
|
||||||
|
|||||||
22
mobile/package-lock.json
generated
@@ -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": {
|
"react-native-maps": {
|
||||||
"version": "0.20.1",
|
"version": "0.20.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-native-maps/-/react-native-maps-0.20.1.tgz",
|
"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": {
|
"react-proxy": {
|
||||||
"version": "1.1.8",
|
"version": "1.1.8",
|
||||||
"resolved": "https://registry.npmjs.org/react-proxy/-/react-proxy-1.1.8.tgz",
|
"resolved": "https://registry.npmjs.org/react-proxy/-/react-proxy-1.1.8.tgz",
|
||||||
|
|||||||
@@ -25,7 +25,9 @@
|
|||||||
"react": "^16.2.0",
|
"react": "^16.2.0",
|
||||||
"react-form-binder": "^1.2.0",
|
"react-form-binder": "^1.2.0",
|
||||||
"react-native": "^0.51.1",
|
"react-native": "^0.51.1",
|
||||||
|
"react-native-iphone-x-helper": "^1.0.2",
|
||||||
"react-native-maps": "^0.20.1",
|
"react-native-maps": "^0.20.1",
|
||||||
|
"react-native-modal": "^5.4.0",
|
||||||
"react-router-native": "^4.2.0",
|
"react-router-native": "^4.2.0",
|
||||||
"react-viro": "^2.4.0",
|
"react-viro": "^2.4.0",
|
||||||
"socket.io-client": "^2.0.4"
|
"socket.io-client": "^2.0.4"
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
ViroARSceneNavigator, ViroARScene, ViroARPlane, ViroBox, ViroText, ViroAmbientLight
|
ViroARSceneNavigator, ViroARScene, ViroARPlane, ViroBox, ViroText, ViroAmbientLight
|
||||||
} from 'react-viro'
|
} from 'react-viro'
|
||||||
import autobind from 'autobind-decorator'
|
import autobind from 'autobind-decorator'
|
||||||
|
import backImage from './images/back.png'
|
||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
helloWorldTextStyle: {
|
helloWorldTextStyle: {
|
||||||
@@ -31,7 +32,9 @@ class WorkItemSceneAR extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<ViroARScene onTrackingInitialized={()=>{this.setState({text : "Hello World!"})}}>
|
<ViroARScene onTrackingInitialized={()=>{this.setState({text : "Hello World!"})}}>
|
||||||
<ViroAmbientLight color="#ffffff" intensity={200}/>
|
<ViroAmbientLight color="#ffffff" intensity={200}/>
|
||||||
<ViroText text={this.state.text} scale={[.5, .5, .5]} position={[0, 0, -1]}
|
<ViroText
|
||||||
|
text={this.state.text} scale={[.5, .5, .5]}
|
||||||
|
position={[0, 0, -1]}
|
||||||
style={styles.helloWorldTextStyle} />
|
style={styles.helloWorldTextStyle} />
|
||||||
<ViroARPlane>
|
<ViroARPlane>
|
||||||
<ViroBox position={[0, .5, 0]} />
|
<ViroBox position={[0, .5, 0]} />
|
||||||
@@ -41,11 +44,7 @@ class WorkItemSceneAR extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Viewer extends React.Component {
|
export class ARViewer extends React.Component {
|
||||||
static navigatorStyle = {
|
|
||||||
navBarHidden: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
}
|
}
|
||||||
@@ -61,11 +60,11 @@ export class Viewer extends React.Component {
|
|||||||
<ViroARSceneNavigator
|
<ViroARSceneNavigator
|
||||||
style={{ width: '100%', height: '100%' }}
|
style={{ width: '100%', height: '100%' }}
|
||||||
apiKey='06F37B6A-74DA-4A83-965A-7DE2209A5C46'
|
apiKey='06F37B6A-74DA-4A83-965A-7DE2209A5C46'
|
||||||
initialScene={{ scene: WorkItemSceneAR }} debug={true} />
|
initialScene={{ scene: WorkItemSceneAR }} />
|
||||||
|
|
||||||
<View style={{position: 'absolute', left: 50, right: 0, top: 50}}>
|
<View style={{position: 'absolute', left: 50, right: 0, top: 50}}>
|
||||||
<TouchableHighlight style={styles.buttons} onPress={this._handlePress} underlayColor={'#00000000'} >
|
<TouchableHighlight style={styles.buttons} onPress={this._handlePress} underlayColor={'#00000000'} >
|
||||||
<Image source={require("./images/back_arrow.png")} />
|
<Image source={backImage} />
|
||||||
</TouchableHighlight>
|
</TouchableHighlight>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
1
mobile/src/ARViewer/index.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { ARViewer } from './ARViewer'
|
||||||
@@ -1,23 +1,8 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { StyleSheet, View, TouchableOpacity, Image, ScrollView } from 'react-native'
|
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 {
|
export class Activity extends React.Component {
|
||||||
static navigatorButtons = {
|
|
||||||
leftButtons: [
|
|
||||||
{
|
|
||||||
id: 'cancel',
|
|
||||||
title: 'Cancel',
|
|
||||||
}
|
|
||||||
],
|
|
||||||
rightButtons: [
|
|
||||||
{
|
|
||||||
id: 'done',
|
|
||||||
title: 'Done',
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
static styles = StyleSheet.create({
|
static styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
height: '100%',
|
height: '100%',
|
||||||
@@ -28,20 +13,10 @@ export class Activity extends React.Component {
|
|||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
this.props.navigator.setOnNavigatorEvent(this._handleNavigatorEvent.bind(this))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleNavigatorEvent(event) {
|
_handlePushButton(event) {
|
||||||
switch (event.id) {
|
this.props.history.goBack()
|
||||||
case 'cancel':
|
|
||||||
case 'done':
|
|
||||||
this.props.navigator.pop()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_handlePressButton() {
|
|
||||||
this.props.navigator.pop()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
1
mobile/src/Activity/index.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { Activity } from './Activity'
|
||||||
42
mobile/src/Auth/DefaultRoute.js
Normal file
@@ -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 (
|
||||||
|
<Route path='/' render={() => (
|
||||||
|
path ? <Redirect to={path} /> : null
|
||||||
|
)} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Platform, StyleSheet, Text, Image, Switch, TextInput,
|
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 logoImage from './images/deighton.png'
|
||||||
import { FormBinder } from 'react-form-binder'
|
import { FormBinder } from 'react-form-binder'
|
||||||
import { api } from '../API'
|
import { api } from '../API'
|
||||||
@@ -8,10 +9,6 @@ import { BoundSwitch, BoundInput, BoundButton } from '../ui'
|
|||||||
import autobind from 'autobind-decorator'
|
import autobind from 'autobind-decorator'
|
||||||
|
|
||||||
export class Login extends React.Component {
|
export class Login extends React.Component {
|
||||||
static navigatorStyle = {
|
|
||||||
navBarHidden: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
static bindings = {
|
static bindings = {
|
||||||
email: {
|
email: {
|
||||||
alwaysGet: true,
|
alwaysGet: true,
|
||||||
@@ -69,25 +66,37 @@ export class Login extends React.Component {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
this.state = {
|
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
|
@autobind
|
||||||
handleLogin() {
|
handleLogin() {
|
||||||
let obj = this.state.binder.getModifiedFieldValues()
|
let obj = this.state.binder.getModifiedFieldValues()
|
||||||
let { navigator } = this.props
|
let { history } = this.props
|
||||||
|
|
||||||
if (obj) {
|
if (obj) {
|
||||||
api.login(obj.email, obj.password, obj.rememberMe).then((user) => {
|
api.login(obj.email, obj.password, obj.rememberMe).then((user) => {
|
||||||
this.props.navigator.dismissAllModals()
|
history.replace('/home')
|
||||||
}).catch((error) => {
|
}).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() {
|
render() {
|
||||||
|
const { messageModal } = this.state
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<KeyboardAvoidingView style={Login.styles.page} behavior='padding'
|
<KeyboardAvoidingView style={Login.styles.page} behavior='padding'
|
||||||
keyboardVerticalOffset={Platform.select({ios: 0, android: -220})}>
|
keyboardVerticalOffset={Platform.select({ios: 0, android: -220})}>
|
||||||
@@ -104,6 +113,12 @@ export class Login extends React.Component {
|
|||||||
<View style={Login.styles.buttonRow}>
|
<View style={Login.styles.buttonRow}>
|
||||||
<BoundButton title='Login' name='login' width='100%' onPress={this.handleLogin} binder={this.state.binder} />
|
<BoundButton title='Login' name='login' width='100%' onPress={this.handleLogin} binder={this.state.binder} />
|
||||||
</View>
|
</View>
|
||||||
|
<MessageModal
|
||||||
|
open={!!messageModal}
|
||||||
|
icon={messageModal ? messageModal.icon : ''}
|
||||||
|
message={messageModal ? messageModal.message : ''}
|
||||||
|
detail={messageModal ? messageModal.detail : ''}
|
||||||
|
onDismiss={messageModal && this.handleMessageDismiss} />
|
||||||
</KeyboardAvoidingView>
|
</KeyboardAvoidingView>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
21
mobile/src/Auth/Logout.js
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
41
mobile/src/Auth/ProtectedRoute.js
Normal file
@@ -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 <Route {...this.props} />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Can add redirect back in here - see website
|
||||||
|
return <Redirect to='/login' />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
4
mobile/src/Auth/index.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export { Login } from './Login'
|
||||||
|
export { Logout } from './Logout'
|
||||||
|
export { ProtectedRoute } from './ProtectedRoute'
|
||||||
|
export { DefaultRoute } from './DefaultRoute'
|
||||||
131
mobile/src/Home/Home.js
Normal file
@@ -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 (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<Header title='Work Item Map' leftButton={{ icon: 'logout', onPress: this.handleLogout }} rightButton={{ icon: 'glasses' }} />
|
||||||
|
<MapView
|
||||||
|
style={{
|
||||||
|
width: '100%', height: '50%',
|
||||||
|
}}
|
||||||
|
zoomControlEnabled
|
||||||
|
initialRegion={{
|
||||||
|
latitude: 43.653908,
|
||||||
|
longitude: -79.384293,
|
||||||
|
latitudeDelta: 0.0922,
|
||||||
|
longitudeDelta: 0.0421,
|
||||||
|
}}>
|
||||||
|
{
|
||||||
|
data.map(marker => (
|
||||||
|
<Marker
|
||||||
|
key={marker.key}
|
||||||
|
coordinate={marker.latlng}
|
||||||
|
title={marker.title}
|
||||||
|
description={marker.location}
|
||||||
|
image={pinImage}
|
||||||
|
anchor={{x: 0.5, y: 1.0}} />
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</MapView>
|
||||||
|
<View style={{ flexDirection: 'row', alignItems: 'center', width: '100%', height: 40, backgroundColor: '#F4F4F4' }}>
|
||||||
|
<Icon name='search' size={16} style={{marginLeft: 10, marginRight: 5, tintColor: 'gray' }} />
|
||||||
|
<TextInput style={{ flexGrow: 1, height: '100%' }} placeholder='Search' />
|
||||||
|
<Icon name='cancel' size={16} style={{marginLeft: 5, marginRight: 10, tintColor: 'gray' }} />
|
||||||
|
</View>
|
||||||
|
<FlatList
|
||||||
|
style={{ width: '100%', flexGrow: 1 }}
|
||||||
|
data={data}
|
||||||
|
renderItem={({item, index}) => {
|
||||||
|
return (
|
||||||
|
<View style={{ flexDirection: 'row', height: 50 }}>
|
||||||
|
<Text style={{ fontSize: 8, width: 45, marginLeft: 5, alignSelf: 'center' }}>{item.state.toUpperCase()}</Text>
|
||||||
|
<View style={{ width: '75%', flexDirection: 'column' }}>
|
||||||
|
<Text style={{ fontSize: 20 }}>{item.title}</Text>
|
||||||
|
<Text style={{ fontSize: 14, color: 'gray' }}>{item.location}</Text>
|
||||||
|
</View>
|
||||||
|
<TouchableOpacity style={{ alignSelf: 'center' }} onPress={() => (this._handleItemSelect(item, index))} >
|
||||||
|
<Icon name='rightArrow' size={16} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}} />
|
||||||
|
<View style={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
width: '100%',
|
||||||
|
height: 45,
|
||||||
|
backgroundColor: '#F4F4F4',
|
||||||
|
}}>
|
||||||
|
<TouchableOpacity onPress={this._handleMyLocation}>
|
||||||
|
<Icon name='center' size={24} style={{ marginLeft: 15, tintColor: 'gray' }} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
<Text style={{ color: 'gray', fontSize: 20, }}>Hide List</Text>
|
||||||
|
<TouchableOpacity onPress={this._handleAdminButton}>
|
||||||
|
<Icon name='settings' size={24} style={{ marginRight: 15, tintColor: 'gray' }} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
1
mobile/src/Home/index.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { Home } from './Home'
|
||||||
62
mobile/src/Modal/MessageModal.js
Normal file
@@ -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 (
|
||||||
|
<Modal isVisible={open}>
|
||||||
|
<View style={{ alignSelf: 'center', padding: 5, backgroundColor: '#FFFFFF', flexDirection: 'row' }}>
|
||||||
|
<Icon name={icon} size={130} margin={3} />
|
||||||
|
<View style={{ marginLeft: 20, marginRight: 20, flexGrow: 1, flexDirection: 'column' }}>
|
||||||
|
<Text style={{ marginTop: 5, fontSize: 18, alignSelf: 'center' }}>{message}</Text>
|
||||||
|
<Text style={{ marginTop: 20, alignSelf: 'center' }}>{detail}</Text>
|
||||||
|
<TouchableOpacity onPress={this.handleButtonPress}
|
||||||
|
style={{
|
||||||
|
alignSelf: 'center',
|
||||||
|
backgroundColor: 'blue',
|
||||||
|
marginTop: 20,
|
||||||
|
marginBottom: 10,
|
||||||
|
justifyContent: 'center',
|
||||||
|
paddingHorizontal: 10,
|
||||||
|
height: 40,
|
||||||
|
width: 100,
|
||||||
|
backgroundColor: '#3BB0FD'
|
||||||
|
}}>
|
||||||
|
<Text style={{ alignSelf: 'center', color: 'black' }}>OK</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
1
mobile/src/Modal/index.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { MessageModal } from './MessageModal'
|
||||||
@@ -2,21 +2,6 @@ import React from 'react'
|
|||||||
import { StyleSheet, View, TouchableOpacity, Image, ScrollView, Picker, Text } from 'react-native'
|
import { StyleSheet, View, TouchableOpacity, Image, ScrollView, Picker, Text } from 'react-native'
|
||||||
|
|
||||||
export class WorkItem extends React.Component {
|
export class WorkItem extends React.Component {
|
||||||
static navigatorButtons = {
|
|
||||||
leftButtons: [
|
|
||||||
{
|
|
||||||
id: 'cancel',
|
|
||||||
title: 'Cancel',
|
|
||||||
}
|
|
||||||
],
|
|
||||||
rightButtons: [
|
|
||||||
{
|
|
||||||
id: 'done',
|
|
||||||
title: 'Done',
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
static styles = StyleSheet.create({
|
static styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
height: '100%',
|
height: '100%',
|
||||||
@@ -27,20 +12,6 @@ export class WorkItem extends React.Component {
|
|||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(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() {
|
render() {
|
||||||
71
mobile/src/WorkItem/WorkItemList.js
Normal file
@@ -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 (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<FlatList
|
||||||
|
style={{ width: '100%', flexGrow: 1, paddingTop: 20, paddingBottom: 20 }}
|
||||||
|
data={data}
|
||||||
|
renderItem={({item, index}) => {
|
||||||
|
return (
|
||||||
|
<View style={{ flexDirection: 'row', height: 50 }}>
|
||||||
|
<Text style={{ fontSize: 8, width: 45, marginLeft: 15, alignSelf: 'center' }}>{item.state.toUpperCase()}</Text>
|
||||||
|
<View style={{ flexDirection: 'column', width: '75%' }}>
|
||||||
|
<Text style={{ fontSize: 20 }}>{Admin.inspectionTypes[item.type].title}</Text>
|
||||||
|
<Text style={{ fontSize: 14, color: 'gray' }}>{item.location}</Text>
|
||||||
|
</View>
|
||||||
|
<TouchableOpacity style={{ alignSelf: 'center' }} onPress={() => (this._handleItemSelect(item, index))} >
|
||||||
|
<Image source={rightArrowImage} style={{ width: 16, height: 16 }} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}} />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
3
mobile/src/WorkItem/index.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export { WorkItem } from './WorkItem'
|
||||||
|
export { WorkItemList } from './WorkItemList'
|
||||||
|
|
||||||
@@ -1,11 +1,15 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { View, StyleSheet, Text, TouchableHighlight, Image } from 'react-native'
|
import { View, StyleSheet } from 'react-native'
|
||||||
import {
|
import {
|
||||||
ViroARSceneNavigator, ViroARScene, ViroARPlane, ViroBox
|
ViroARSceneNavigator, ViroARScene, ViroARPlane, ViroBox
|
||||||
} from 'react-viro'
|
} 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 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
|
// See https://github.com/facebook/react-native/issues/12981
|
||||||
console.ignoredYellowBox = [
|
console.ignoredYellowBox = [
|
||||||
@@ -16,13 +20,17 @@ export default class App extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<NativeRouter>
|
<NativeRouter>
|
||||||
<View style={styles.container}>
|
<View style={{ width: '100%', height: '100%' }}>
|
||||||
<Route exact path="/" component={Home}/>
|
<Switch>
|
||||||
<Route path="/viewer" component={Viewer}/>
|
<Route exact path='/login' component={Login}/>
|
||||||
<Route path="/workitem" component={WorkItem}/>
|
<Route exact path='/logout' component={Logout}/>
|
||||||
<Route path="/admin" component={Admin}/>
|
<ProtectedRoute exact path='/home' component={Home}/>
|
||||||
<Route path="/login" component={Login}/>
|
<ProtectedRoute exact path='/viewer' component={ARViewer}/>
|
||||||
<Route path="/activity" component={Activity}/>
|
<ProtectedRoute exact path='/activity' component={Activity}/>
|
||||||
|
<ProtectedRoute exact admin path='/workitem' component={WorkItem}/>
|
||||||
|
<ProtectedRoute exact admin path='/workitemlist' component={WorkItemList}/>
|
||||||
|
<DefaultRoute />
|
||||||
|
</Switch>
|
||||||
</View>
|
</View>
|
||||||
</NativeRouter>
|
</NativeRouter>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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 (
|
|
||||||
<View style={Admin.styles.container}>
|
|
||||||
<FlatList
|
|
||||||
style={{ width: '100%', flexGrow: 1, paddingTop: 20, paddingBottom: 20 }}
|
|
||||||
data={Admin.data}
|
|
||||||
renderItem={({item, index}) => {
|
|
||||||
return (
|
|
||||||
<View style={{ flexDirection: 'row', height: 50 }}>
|
|
||||||
<Text style={{ fontSize: 8, width: 45, marginLeft: 15, alignSelf: 'center' }}>{item.state.toUpperCase()}</Text>
|
|
||||||
<View style={{ flexDirection: 'column', width: '75%' }}>
|
|
||||||
<Text style={{ fontSize: 20 }}>{Admin.inspectionTypes[item.type].title}</Text>
|
|
||||||
<Text style={{ fontSize: 14, color: 'gray' }}>{item.location}</Text>
|
|
||||||
</View>
|
|
||||||
<TouchableOpacity style={{ alignSelf: 'center' }} onPress={() => (this._handleItemSelect(item, index))} >
|
|
||||||
<Image source={rightArrowImage} style={{ width: 16, height: 16 }} />
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
}} />
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 (
|
|
||||||
<View style={Login.styles.page}>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 (
|
|
||||||
<View style={Home.styles.container}>
|
|
||||||
<MapView
|
|
||||||
style={{
|
|
||||||
width: '100%', height: '50%',
|
|
||||||
}}
|
|
||||||
zoomControlEnabled
|
|
||||||
initialRegion={{
|
|
||||||
latitude: 43.653908,
|
|
||||||
longitude: -79.384293,
|
|
||||||
latitudeDelta: 0.0922,
|
|
||||||
longitudeDelta: 0.0421,
|
|
||||||
}}>
|
|
||||||
{
|
|
||||||
Home.data.map(marker => (
|
|
||||||
<Marker
|
|
||||||
key={marker.key}
|
|
||||||
coordinate={marker.latlng}
|
|
||||||
title={marker.title}
|
|
||||||
description={marker.location}
|
|
||||||
image={pinImage}
|
|
||||||
anchor={{x: 0.5, y: 1.0}} />
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</MapView>
|
|
||||||
<View style={{ flexDirection: 'row', alignItems: 'center', width: '100%', height: 40, backgroundColor: '#F4F4F4' }}>
|
|
||||||
<Image source={searchImage} style={{ width: 16, height: 16, tintColor: 'gray', marginLeft: 10, marginRight: 5 }} />
|
|
||||||
<TextInput style={{ flexGrow: 1, height: '100%' }} placeholder='Search' />
|
|
||||||
<Image source={cancelImage} style={{ width: 16, height: 16, marginLeft: 5, marginRight: 10 }} />
|
|
||||||
</View>
|
|
||||||
<FlatList
|
|
||||||
style={{ width: '100%', flexGrow: 1 }}
|
|
||||||
data={Home.data}
|
|
||||||
renderItem={({item, index}) => {
|
|
||||||
return (
|
|
||||||
<View style={{ flexDirection: 'row', height: 50 }}>
|
|
||||||
<Text style={{ fontSize: 8, width: 45, marginLeft: 5, alignSelf: 'center' }}>{item.state.toUpperCase()}</Text>
|
|
||||||
<View style={{ width: '75%', flexDirection: 'column' }}>
|
|
||||||
<Text style={{ fontSize: 20 }}>{item.title}</Text>
|
|
||||||
<Text style={{ fontSize: 14, color: 'gray' }}>{item.location}</Text>
|
|
||||||
</View>
|
|
||||||
<TouchableOpacity style={{ alignSelf: 'center' }} onPress={() => (this._handleItemSelect(item, index))} >
|
|
||||||
<Image source={rightArrowImage} style={{ width: 16, height: 16 }} />
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
}} />
|
|
||||||
<View style={{
|
|
||||||
flexDirection: 'row',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
alignItems: 'center',
|
|
||||||
width: '100%',
|
|
||||||
height: 45,
|
|
||||||
backgroundColor: '#F4F4F4',
|
|
||||||
}}>
|
|
||||||
<TouchableOpacity onPress={this._handleMyLocation}>
|
|
||||||
<Image source={centerImage} style={{ height: 24, width: 24, marginLeft: 15, tintColor: 'gray' }} />
|
|
||||||
</TouchableOpacity>
|
|
||||||
<Text style={{ color: 'gray', fontSize: 20, }}>Hide List</Text>
|
|
||||||
<TouchableOpacity onPress={this._handleAdminButton}>
|
|
||||||
<Image source={settingsImage} style={{ height: 24, width: 24, marginRight: 15, tintColor: 'gray' }} />
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 2.6 KiB |
@@ -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)
|
|
||||||
}
|
|
||||||
39
mobile/src/ui/Header.js
Normal file
@@ -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 (
|
||||||
|
<View style={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
width: '100%',
|
||||||
|
height: 45,
|
||||||
|
backgroundColor: '#F4F4F4',
|
||||||
|
...ifIphoneX({ marginTop: 50 }, { marginTop: 20 })
|
||||||
|
}}>
|
||||||
|
<TouchableOpacity onPress={leftButton.onPress}>
|
||||||
|
<Icon name={leftButton.icon} size={24} style={{ marginLeft: 15, tintColor: 'gray' }} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
<Text style={{ color: 'gray', fontSize: 18, }}>{title}</Text>
|
||||||
|
<TouchableOpacity onPress={rightButton.onPress}>
|
||||||
|
<Icon name={rightButton.icon} size={24} style={{ marginRight: 15, tintColor: 'gray' }} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
37
mobile/src/ui/Icon.js
Normal file
@@ -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 <Image style={[{ width: size, height: size, margin }, style]} source={source} resizeMode='stretch' />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
BIN
mobile/src/ui/images/back.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
BIN
mobile/src/ui/images/hand.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
@@ -1,3 +1,5 @@
|
|||||||
export { BoundSwitch } from './BoundSwitch'
|
export { BoundSwitch } from './BoundSwitch'
|
||||||
export { BoundInput } from './BoundInput'
|
export { BoundInput } from './BoundInput'
|
||||||
export { BoundButton } from './BoundButton'
|
export { BoundButton } from './BoundButton'
|
||||||
|
export { Icon } from './Icon'
|
||||||
|
export { Header } from './Header'
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ export class AuthRoutes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let cr = credential()
|
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) {
|
if (isValid) {
|
||||||
user.loginToken = loginToken.pack(user._id.toString(), user.email)
|
user.loginToken = loginToken.pack(user._id.toString(), user.email)
|
||||||
|
|||||||
@@ -20,14 +20,14 @@ export class DefaultRoute extends Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const user = api.loggedInUser
|
const user = api.loggedInUser
|
||||||
let redirect = null
|
let path = null
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
if (!user.pending) {
|
if (!user.pending) {
|
||||||
redirect = <Redirect to={user.administrator ? '/home' : '/profile'} />
|
path = user.administrator ? '/home' : '/profile'
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
redirect = <Redirect to='/login' />
|
path = '/login'
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -37,7 +37,7 @@ export class DefaultRoute extends Component {
|
|||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<Column.Item grow />
|
<Column.Item grow />
|
||||||
{redirect}
|
{path ? <Redirect to={path} /> : null}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Route, Redirect } from 'react-router-dom'
|
import { Route, Redirect } from 'react-router'
|
||||||
import { PropTypes } from 'prop-types'
|
import { PropTypes } from 'prop-types'
|
||||||
import { api } from 'src/API'
|
import { api } from 'src/API'
|
||||||
import autobind from 'autobind-decorator'
|
import autobind from 'autobind-decorator'
|
||||||
|
|||||||