diff --git a/design/Deighton AR Design.sketch b/design/Deighton AR Design.sketch index 9b9ff27..356b49e 100644 Binary files a/design/Deighton AR Design.sketch and b/design/Deighton AR Design.sketch differ diff --git a/mobile/src/Activity/Activity.js b/mobile/src/Activity/Activity.js index 6b360ca..8727508 100644 --- a/mobile/src/Activity/Activity.js +++ b/mobile/src/Activity/Activity.js @@ -267,7 +267,7 @@ export class Activity extends React.Component { if (this.state.progressModal) { this.setState({ uploadPercent: Math.round( - uploadData.uploadedChunks / uploadData.numberOfChunks * 100 + (uploadData.uploadedChunks / uploadData.numberOfChunks) * 100 ), }) return true diff --git a/mobile/src/Auth/Login.js b/mobile/src/Auth/Login.js index cc0c395..93b90be 100644 --- a/mobile/src/Auth/Login.js +++ b/mobile/src/Auth/Login.js @@ -4,10 +4,7 @@ import { StyleSheet, Text, Image, - Switch, - TextInput, View, - Button, TouchableWithoutFeedback, } from "react-native" import { MessageModal, ApiModal, WaitModal } from "../Modal" @@ -18,7 +15,6 @@ import { BoundSwitch, BoundInput, BoundButton } from "../ui" import KeyboardSpacer from "react-native-keyboard-spacer" import { versionInfo } from "../version" import { reactAutoBind } from "auto-bind2" -import { isIphoneX } from "react-native-iphone-x-helper" import { config } from "../config" export class Login extends React.Component { diff --git a/mobile/src/Home/Home.js b/mobile/src/Home/Home.js index 2970201..6e53813 100644 --- a/mobile/src/Home/Home.js +++ b/mobile/src/Home/Home.js @@ -31,6 +31,7 @@ import KeyboardSpacer from "react-native-keyboard-spacer" import hardhatPinImage from "./images/hardhat-pin.png" import clipboardPinImage from "./images/clipboard-pin.png" import questionPinImage from "./images/question-pin.png" +import locationImage from "./images/location.png" import moment from "moment" const neverAskForLocationPermissionKeyName = "NeverAskForLocationPermission" @@ -175,8 +176,8 @@ export class Home extends React.Component { } handleWorkItemsListPress() { - if (this.currentPosition) { - const { coords } = this.currentPosition + if (this.state.currentPosition) { + const { coords } = this.state.currentPosition this.props.history.push( `/workItemList?latLng=${coords.latitude},${coords.longitude}` @@ -209,13 +210,13 @@ export class Home extends React.Component { } handleGlassesPress() { - const { sections: workItems } = this.state + const { sections: workItems, currentPosition } = this.state - if (this.currentPosition) { + if (currentPosition) { const { latitude: latitude1, longitude: longitude1, - } = this.currentPosition.coords + } = currentPosition.coords let closestWorkItem = config.alwaysShowWorkItemInAR ? workItems[0] : null let shortestDistance = config.minDistanceToItem @@ -247,8 +248,10 @@ export class Home extends React.Component { } handleMyLocationPress() { - if (this.currentPosition && this.isMapReady) { - const coords = this.currentPosition.coords + const { currentPosition } = this.state + + if (currentPosition && this.isMapReady) { + const { coords } = currentPosition this.mapView.animateToCoordinate({ latitude: coords.latitude, @@ -278,12 +281,14 @@ export class Home extends React.Component { } handlePositionUpdate(position) { - this.currentPosition = position + this.setState({ currentPosition: position }) } getCoordinateDistance(coordinate) { - if (this.currentPosition) { - const coords = this.currentPosition.coords + const { currentPosition } = this.state + + if (currentPosition) { + const { coords } = currentPosition const { latitude, longitude } = coordinate return ( @@ -308,6 +313,7 @@ export class Home extends React.Component { waitModal, haveCameraPermission, haveLocationPermission, + currentPosition, } = this.state return ( @@ -334,7 +340,7 @@ export class Home extends React.Component { showWorkItems && { height: "40%" }, !showWorkItems && { flexGrow: 1 }, ]} - showsUserLocation + showsUserLocation={false} showsBuildings={false} showsTraffic={false} showsIndoors={false} @@ -370,6 +376,14 @@ export class Home extends React.Component { ))} + {currentPosition && ( + + )} {activity.teamName + " | " + - dotify(moment(activity.createdAt).format())} + dotify(moment(activity.createdAt).format(), -15)} Animated.sequence([ Animated.timing(value, { - toValue: 0.2, + toValue: 0.0, duration: 800, delay: 200 * i, easing: Easing.out(Easing.sin), diff --git a/mobile/src/ui/Geolocation.js b/mobile/src/ui/Geolocation.js index 51cdab9..68829fd 100644 --- a/mobile/src/ui/Geolocation.js +++ b/mobile/src/ui/Geolocation.js @@ -1,6 +1,7 @@ import React, { Component } from "react" -import { Image, View } from "react-native" import PropTypes from "prop-types" +import { config } from "../config" +import { reactAutoBind } from "auto-bind2" export class Geolocation extends Component { static propTypes = { @@ -14,29 +15,36 @@ export class Geolocation extends Component { constructor(props) { super(props) - this.watchId = -1 + reactAutoBind(this) + + if (props.watch) { + this.intervalId = setInterval(this.updateLocation, config.geoSampleDelay) + } else { + this.intervalId = -1 + } } componentDidMount() { - navigator.geolocation.getCurrentPosition((position) => { - this.props.onUpdate(position) - - if (this.props.watch) { - this.watchId = navigator.geolocation.watchPosition((position) => - this.props.onUpdate(position) - ) - } - }) + this.updateLocation() } componentWillUnmount() { - if (this.watchId !== -1) { - navigator.geolocation.clearWatch(this.watchId) - navigator.geolocation.stopObserving() - this.watchId = -1 + if (this.intervalId !== -1) { + clearInterval(this.intervalId) + this.intervalId = -1 } } + updateLocation() { + navigator.geolocation.getCurrentPosition( + (position) => { + this.props.onUpdate(position) + }, + null, + { distanceFilter: 5, maximumAge: 0, enableHighAccuracy: true } + ) + } + render() { return null } diff --git a/mobile/src/util.js b/mobile/src/util.js index ed73071..fa7c636 100644 --- a/mobile/src/util.js +++ b/mobile/src/util.js @@ -1,13 +1,13 @@ export const geoDistance = (lat1, lng1, lat2, lng2, unit) => { - var radlat1 = Math.PI * lat1 / 180 - var radlat2 = Math.PI * lat2 / 180 + var radlat1 = (Math.PI * lat1) / 180 + var radlat2 = (Math.PI * lat2) / 180 var theta = lng1 - lng2 - var radtheta = Math.PI * theta / 180 + var radtheta = (Math.PI * theta) / 180 var dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta) dist = Math.acos(dist) - dist = dist * 180 / Math.PI + dist = (dist * 180) / Math.PI dist = dist * 60 * 1.1515 if (unit == "K") { dist = dist * 1.609344 @@ -56,13 +56,22 @@ export const pad = (num, size) => { return s } -export const dotify = (s) => { +export const dotify = (s, len = 30) => { + let pre = false + + if (len < 0) { + pre = true + len = -len + } + if (!s) { return "..." - } else if (s.length < 30) { + } else if (s.length < len) { return s + } else if (pre) { + return "..." + s.slice(s.length - len - 3) } else { - return s.substring(0, 26) + "..." + return s.slice(0, len - 3) + "..." } } diff --git a/server/src/api/routes/UserRoutes.js b/server/src/api/routes/UserRoutes.js index 6328342..a168ed2 100644 --- a/server/src/api/routes/UserRoutes.js +++ b/server/src/api/routes/UserRoutes.js @@ -112,7 +112,7 @@ export class UserRoutes { const isSelf = _id === req.user._id const isAdmin = req.user.administrator - // User can see themselves, otherwise must be super user + // User can see themselves, otherwise must be admin if (!isSelf && !isAdmin) { throw createError.Forbidden() } @@ -185,8 +185,8 @@ export class UserRoutes { throw createError.Forbidden() } - if (isSelf && !isAdmin) { - throw createError.BadRequest("Cannot modify own administrator level") + if (isSelf && isAdmin && !req.body.administrator) { + throw createError.BadRequest("Cannot remove own administrator level") } const User = this.db.User @@ -199,7 +199,7 @@ export class UserRoutes { // We don't allow direct updates to the email field so remove it if present const userUpdates = new User(req.body) - delete userUpdates.email + userUpdates.email = undefined user.merge(userUpdates) const savedUser = await user.save() diff --git a/website/src/ui/Loader.js b/website/src/ui/Loader.js index 644dac4..c11964b 100644 --- a/website/src/ui/Loader.js +++ b/website/src/ui/Loader.js @@ -1,7 +1,7 @@ -import React from 'react' -import Radium from 'radium' -import { colorInfo, sizeInfo } from 'ui/style' -import anime from 'animejs' +import React from "react" +import Radium from "radium" +import { colorInfo, sizeInfo } from "ui/style" +import anime from "animejs" @Radium export class Loader extends React.Component { @@ -13,30 +13,31 @@ export class Loader extends React.Component { anime({ targets: elem, scale: [ - { value: 0.2, duration: 800, easing: 'easeOutSine', delay: 200 * i }, - { value: 1.0, duration: 800, easing: 'easeOutSine' }, - { value: 1.0, duration: 200 * (2 - i) } + { value: 0.0, duration: 800, easing: "easeOutSine", delay: 200 * i }, + { value: 1.0, duration: 800, easing: "easeOutSine" }, + { value: 1.0, duration: 200 * (2 - i) }, ], - loop: true + loop: true, }) } return (
- { - [0, 1, 2].map((i) => ( - addAnimation(elem, i)} - style={{ - display: 'inline-block', - marginLeft: i > 0 ? spacing : 0, - width: size, - height: size, - background: colorInfo.textInverse, - borderSize: 0, - borderRadius: '100%' - }} /> - )) - } + {[0, 1, 2].map((i) => ( + addAnimation(elem, i)} + style={{ + display: "inline-block", + marginLeft: i > 0 ? spacing : 0, + width: size, + height: size, + background: colorInfo.textInverse, + borderSize: 0, + borderRadius: "100%", + }} + /> + ))}
) }