Skip to content

Commit

Permalink
feat: add GPS Modal
Browse files Browse the repository at this point in the history
  • Loading branch information
gmaclennan committed Apr 15, 2019
1 parent 0ce7a12 commit 8c94493
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 49 deletions.
36 changes: 7 additions & 29 deletions src/frontend/components/GpsPill.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,9 @@ import { withNavigation, withNavigationFocus } from "react-navigation";

import LocationContext from "../context/LocationContext";
import GpsIcon from "./icons/GpsIcon";
import { getLocationStatus } from "../lib/utils";
import type { LocationStatus } from "../lib/utils";

// If the current position on the app state is more than 60 seconds old then we
// consider it stale and show that the GPS is searching for a new position
const STALE_TIMEOUT = 60 * 1000; // 60 seconds
// If the precision is less than 10 meters then we consider this to be a "good
// position" and we change the UI accordingly
const GOOD_PRECISION = 10; // 10 meters
const ERROR_COLOR = "#FF0000";

const styles = StyleSheet.create({
Expand Down Expand Up @@ -42,12 +38,10 @@ const styles = StyleSheet.create({
}
});

export type Variant = "searching" | "improving" | "good" | "error";

type Props = {
onPress: null | (() => void),
precision?: number,
variant: Variant,
variant: LocationStatus,
isFocused: boolean
};

Expand Down Expand Up @@ -85,32 +79,16 @@ export class GpsPill extends React.PureComponent<Props, State> {

const ConnectedGpsPill = ({ navigation, isFocused }) => (
<LocationContext.Consumer>
{({ position, provider, permission, error }) => {
const precision = position && position.coords.accuracy;
const gpsUnavailable = provider && !provider.gpsAvailable;
const locationServicesDisabled =
provider && !provider.locationServicesEnabled;
const noPermission = permission && permission !== "granted";
const positionStale =
position && Date.now() - position.timestamp > STALE_TIMEOUT;
let variant: Variant;
if (error || gpsUnavailable || locationServicesDisabled || noPermission)
variant = "error";
else if (positionStale) variant = "searching";
else if (
typeof precision === "number" &&
Math.round(precision) <= GOOD_PRECISION
)
variant = "good";
else if (typeof precision === "number") variant = "improving";
else variant = "searching";
{location => {
const locationStatus = getLocationStatus(location);
const precision = location.position && location.position.coords.accuracy;
return (
<GpsPill
onPress={() => navigation.navigate("GpsModal")}
precision={
typeof precision === "number" ? Math.round(precision) : undefined
}
variant={variant}
variant={locationStatus}
isFocused={isFocused}
/>
);
Expand Down
1 change: 1 addition & 0 deletions src/frontend/components/icons/GpsIcon.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const renderProgress = (color: string) => (
indeterminate
color={color}
strokeCap="round"
direction="clockwise"
/>
);
const renderGood = () => (
Expand Down
39 changes: 38 additions & 1 deletion src/frontend/lib/utils.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,40 @@
export function getDisplayName(WrappedComponent) {
// @flow
import type { LocationContextType } from "../context/LocationContext";

export function getDisplayName(WrappedComponent: any) {
return WrappedComponent.displayName || WrappedComponent.name || "Component";
}

// If the current position on the app state is more than 60 seconds old then we
// consider it stale and show that the GPS is searching for a new position
const STALE_TIMEOUT = 60 * 1000; // 60 seconds
// If the precision is less than 10 meters then we consider this to be a "good
// position" and we change the UI accordingly
const GOOD_PRECISION = 10; // 10 meters

export type LocationStatus = "searching" | "improving" | "good" | "error";

export function getLocationStatus({
position,
provider,
permission,
error
}: LocationContextType): LocationStatus {
const precision = position && position.coords.accuracy;
const gpsUnavailable = provider && !provider.gpsAvailable;
const locationServicesDisabled =
provider && !provider.locationServicesEnabled;
const noPermission = permission && permission !== "granted";
const positionStale =
position && Date.now() - position.timestamp > STALE_TIMEOUT;
if (error || gpsUnavailable || locationServicesDisabled || noPermission)
return "error";
else if (positionStale) return "searching";
else if (
typeof precision === "number" &&
Math.round(precision) <= GOOD_PRECISION
)
return "good";
else if (typeof precision === "number") return "improving";
return "searching";
}
79 changes: 60 additions & 19 deletions src/frontend/navigation/GpsModalScreen.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,72 @@
// @flow
import React from "react";
import { View, Text } from "react-native";
import { View, ScrollView, Text, StyleSheet } from "react-native";

import IconButton from "../components/IconButton";
import CloseIcon from "../components/icons/CloseIcon";
import LocationContext from "../context/LocationContext";

const GpsModalScreen = ({ navigation }) => (
<View
style={{
flex: 1,
backgroundColor: "rgba(0,0,0,0.85)"
}}
>
<IconButton onPress={() => navigation.pop()}>
import { getLocationStatus } from "../lib/utils";
import type { LocationStatus } from "../lib/utils";

const styles = StyleSheet.create({
header: {
flex: 1,
height: 60,
marginBottom: 20,
flexDirection: "row",
alignItems: "center"
},
title: {
color: "white",
fontWeight: "700",
fontSize: 20,
backgroundColor: "red",
flex: 1,
textAlign: "center",
marginRight: 60
},
container: {
flex: 1,
backgroundColor: "rgba(0,0,0,0.85)",
flexDirection: "column"
}
});

type HeaderProps = {
onClose: () => void,
variant: LocationStatus
};

const GpsModalHeader = ({ onClose, variant }: HeaderProps) => (
<View style={styles.header}>
<IconButton onPress={onClose}>
<CloseIcon color="white" />
</IconButton>
<LocationContext.Consumer>
{location => {
console.log("render");
return (
<Text style={{ color: "white" }}>
{JSON.stringify(location, null, 2)}
</Text>
);
}}
</LocationContext.Consumer>
<Text numberOfLines={1} style={styles.title}>
Current Position
</Text>
</View>
);

type Props = {
navigation: any
};

const GpsModalScreen = ({ navigation }: Props) => (
<LocationContext.Consumer>
{location => (
<ScrollView style={styles.container}>
<GpsModalHeader
onClose={() => navigation.pop()}
variant={getLocationStatus(location)}
/>
<Text style={{ color: "white" }}>
{JSON.stringify(location, null, 2)}
</Text>
</ScrollView>
)}
</LocationContext.Consumer>
);

export default GpsModalScreen;

0 comments on commit 8c94493

Please sign in to comment.