feat(follow-location): wip
This commit is contained in:
parent
ca3a2c8fcc
commit
c30c0b0482
5 changed files with 253 additions and 2 deletions
|
@ -25,6 +25,8 @@ const ALERT_FIELDS_FRAGMENT = gql`
|
||||||
acknowledgedRelativeCount
|
acknowledgedRelativeCount
|
||||||
acknowledgedAroundCount
|
acknowledgedAroundCount
|
||||||
acknowledgedConnectCount
|
acknowledgedConnectCount
|
||||||
|
followLocation
|
||||||
|
followLocationRan
|
||||||
accessCode
|
accessCode
|
||||||
userId
|
userId
|
||||||
keepOpenAt
|
keepOpenAt
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// related to services/tasks/src/geocode/config.js
|
// related to services/tasks/src/geocode/config.js
|
||||||
export const TRACK_MOVE = 100;
|
export const TRACK_MOVE = 10;
|
||||||
export const DEFAULT_DEVICE_RADIUS_ALL = 500;
|
export const DEFAULT_DEVICE_RADIUS_ALL = 500;
|
||||||
export const DEFAULT_DEVICE_RADIUS_REACH = 25000;
|
export const DEFAULT_DEVICE_RADIUS_REACH = 25000;
|
||||||
export const MAX_BASEUSER_DEVICE_TRACKING = 25000;
|
export const MAX_BASEUSER_DEVICE_TRACKING = 25000;
|
||||||
|
|
232
src/scenes/AlertCurOverview/FieldFollowLocation.js
Normal file
232
src/scenes/AlertCurOverview/FieldFollowLocation.js
Normal file
|
@ -0,0 +1,232 @@
|
||||||
|
import { useCallback, useEffect, useState, useRef } from "react";
|
||||||
|
import { View, AppState } from "react-native";
|
||||||
|
import { Switch, Button } from "react-native-paper";
|
||||||
|
import { MaterialCommunityIcons } from "@expo/vector-icons";
|
||||||
|
import { useMutation } from "@apollo/client";
|
||||||
|
import * as Location from "expo-location";
|
||||||
|
|
||||||
|
import { UPDATE_ALERT_FOLLOW_LOCATION_MUTATION } from "./gql";
|
||||||
|
import Text from "~/components/Text";
|
||||||
|
import { createStyles, useTheme } from "~/theme";
|
||||||
|
import { usePermissionsState, permissionsActions } from "~/stores";
|
||||||
|
import requestPermissionLocationBackground from "~/permissions/requestPermissionLocationBackground";
|
||||||
|
import requestPermissionLocationForeground from "~/permissions/requestPermissionLocationForeground";
|
||||||
|
import openSettings from "~/lib/native/openSettings";
|
||||||
|
|
||||||
|
export default function FieldFollowLocation({ alert }) {
|
||||||
|
const styles = useStyles();
|
||||||
|
const { colors } = useTheme();
|
||||||
|
const { id: alertId } = alert;
|
||||||
|
|
||||||
|
const [updateAlertFollowLocationMutation] = useMutation(
|
||||||
|
UPDATE_ALERT_FOLLOW_LOCATION_MUTATION,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { locationForeground, locationBackground } = usePermissionsState([
|
||||||
|
"locationForeground",
|
||||||
|
"locationBackground",
|
||||||
|
]);
|
||||||
|
|
||||||
|
const [followLocation, setFollowLocation] = useState(
|
||||||
|
alert.followLocation || false,
|
||||||
|
);
|
||||||
|
const [isRequestingPermissions, setIsRequestingPermissions] = useState(false);
|
||||||
|
|
||||||
|
const prevFollowLocation = useRef(alert.followLocation);
|
||||||
|
useEffect(() => {
|
||||||
|
if (prevFollowLocation.current !== alert.followLocation) {
|
||||||
|
prevFollowLocation.current = alert.followLocation;
|
||||||
|
setFollowLocation(alert.followLocation || false);
|
||||||
|
}
|
||||||
|
}, [alert.followLocation]);
|
||||||
|
|
||||||
|
// Check current permission status
|
||||||
|
const checkPermissionStatus = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
const { status: fgStatus } =
|
||||||
|
await Location.getForegroundPermissionsAsync();
|
||||||
|
const { status: bgStatus } =
|
||||||
|
await Location.getBackgroundPermissionsAsync();
|
||||||
|
|
||||||
|
permissionsActions.setLocationForeground(fgStatus === "granted");
|
||||||
|
permissionsActions.setLocationBackground(bgStatus === "granted");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error checking location permissions:", error);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Check permissions on mount
|
||||||
|
useEffect(() => {
|
||||||
|
checkPermissionStatus();
|
||||||
|
}, [checkPermissionStatus]);
|
||||||
|
|
||||||
|
// Listen for app state changes to refresh permissions when returning from settings
|
||||||
|
useEffect(() => {
|
||||||
|
const handleAppStateChange = (nextAppState) => {
|
||||||
|
if (nextAppState === "active") {
|
||||||
|
// App came to foreground, check permissions
|
||||||
|
checkPermissionStatus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const subscription = AppState.addEventListener(
|
||||||
|
"change",
|
||||||
|
handleAppStateChange,
|
||||||
|
);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
subscription?.remove();
|
||||||
|
};
|
||||||
|
}, [checkPermissionStatus]);
|
||||||
|
|
||||||
|
const requestLocationPermissions = useCallback(async () => {
|
||||||
|
setIsRequestingPermissions(true);
|
||||||
|
try {
|
||||||
|
// Request foreground permission first
|
||||||
|
const foregroundGranted = await requestPermissionLocationForeground();
|
||||||
|
permissionsActions.setLocationForeground(foregroundGranted);
|
||||||
|
|
||||||
|
if (foregroundGranted) {
|
||||||
|
// Request background permission if foreground is granted
|
||||||
|
const backgroundGranted = await requestPermissionLocationBackground();
|
||||||
|
permissionsActions.setLocationBackground(backgroundGranted);
|
||||||
|
return backgroundGranted;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error requesting location permissions:", error);
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
setIsRequestingPermissions(false);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const toggleFollowLocation = useCallback(
|
||||||
|
async (value) => {
|
||||||
|
if (value) {
|
||||||
|
// Check if permissions are granted when enabling
|
||||||
|
if (!locationForeground || !locationBackground) {
|
||||||
|
const permissionsGranted = await requestLocationPermissions();
|
||||||
|
if (!permissionsGranted) {
|
||||||
|
// Don't enable if permissions weren't granted
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setFollowLocation(value);
|
||||||
|
updateAlertFollowLocationMutation({
|
||||||
|
variables: { alertId, followLocation: value },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[
|
||||||
|
alertId,
|
||||||
|
updateAlertFollowLocationMutation,
|
||||||
|
locationForeground,
|
||||||
|
locationBackground,
|
||||||
|
requestLocationPermissions,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
const hasRequiredPermissions = locationForeground && locationBackground;
|
||||||
|
const showPermissionWarning = followLocation && !hasRequiredPermissions;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<View style={styles.content}>
|
||||||
|
<MaterialCommunityIcons
|
||||||
|
name="crosshairs-gps"
|
||||||
|
style={styles.icon}
|
||||||
|
size={20}
|
||||||
|
/>
|
||||||
|
<Text style={styles.label}>Suivre ma localisation</Text>
|
||||||
|
<Switch
|
||||||
|
value={followLocation}
|
||||||
|
onValueChange={toggleFollowLocation}
|
||||||
|
color={colors.primary}
|
||||||
|
disabled={isRequestingPermissions}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{showPermissionWarning && (
|
||||||
|
<View style={styles.warningContainer}>
|
||||||
|
<View style={styles.warningContent}>
|
||||||
|
<MaterialCommunityIcons
|
||||||
|
name="alert-circle-outline"
|
||||||
|
style={styles.warningIcon}
|
||||||
|
size={16}
|
||||||
|
/>
|
||||||
|
<Text style={styles.warningText}>
|
||||||
|
Permissions de localisation requises
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<Button
|
||||||
|
mode="outlined"
|
||||||
|
onPress={openSettings}
|
||||||
|
style={styles.settingsButton}
|
||||||
|
labelStyle={styles.settingsButtonLabel}
|
||||||
|
compact
|
||||||
|
>
|
||||||
|
Paramétrer
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const useStyles = createStyles(
|
||||||
|
({ wp, hp, scaleText, fontSize, theme: { colors } }) => ({
|
||||||
|
container: {
|
||||||
|
marginVertical: hp(1),
|
||||||
|
paddingHorizontal: wp(4),
|
||||||
|
paddingVertical: hp(1.5),
|
||||||
|
backgroundColor: colors.surface,
|
||||||
|
borderRadius: 8,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: colors.outline,
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
color: colors.primary,
|
||||||
|
marginRight: wp(2),
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
flex: 1,
|
||||||
|
...scaleText({ fontSize: 16 }),
|
||||||
|
color: colors.onSurface,
|
||||||
|
},
|
||||||
|
warningContainer: {
|
||||||
|
marginTop: hp(1),
|
||||||
|
paddingTop: hp(1),
|
||||||
|
borderTopWidth: 1,
|
||||||
|
borderTopColor: colors.outline,
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
},
|
||||||
|
warningContent: {
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
warningIcon: {
|
||||||
|
color: colors.error,
|
||||||
|
marginRight: wp(1),
|
||||||
|
},
|
||||||
|
warningText: {
|
||||||
|
...scaleText({ fontSize: 14 }),
|
||||||
|
color: colors.error,
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
settingsButton: {
|
||||||
|
marginLeft: wp(2),
|
||||||
|
},
|
||||||
|
settingsButtonLabel: {
|
||||||
|
...scaleText({ fontSize: 12 }),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
|
@ -60,3 +60,17 @@ export const UPDATE_ALERT_SUBJECT_MUTATION = gql`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const UPDATE_ALERT_FOLLOW_LOCATION_MUTATION = gql`
|
||||||
|
mutation updateAlertFollowLocation(
|
||||||
|
$alertId: Int!
|
||||||
|
$followLocation: Boolean!
|
||||||
|
) {
|
||||||
|
updateOneAlert(
|
||||||
|
pk_columns: { id: $alertId }
|
||||||
|
_set: { followLocation: $followLocation }
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
|
@ -50,6 +50,7 @@ import { useMutation } from "@apollo/client";
|
||||||
|
|
||||||
import FieldLevel from "./FieldLevel";
|
import FieldLevel from "./FieldLevel";
|
||||||
import FieldSubject from "./FieldSubject";
|
import FieldSubject from "./FieldSubject";
|
||||||
|
import FieldFollowLocation from "./FieldFollowLocation";
|
||||||
import MapLinksButton from "~/containers/MapLinksButton";
|
import MapLinksButton from "~/containers/MapLinksButton";
|
||||||
import { useTheme } from "~/theme";
|
import { useTheme } from "~/theme";
|
||||||
|
|
||||||
|
@ -497,6 +498,8 @@ export default withConnectivity(
|
||||||
alert={alert}
|
alert={alert}
|
||||||
setParentScrollEnabled={setParentScrollEnabled}
|
setParentScrollEnabled={setParentScrollEnabled}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<FieldFollowLocation alert={alert} />
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -505,7 +508,7 @@ export default withConnectivity(
|
||||||
<AlertInfoLineLevel alert={alert} isFirst />
|
<AlertInfoLineLevel alert={alert} isFirst />
|
||||||
)}
|
)}
|
||||||
<AlertInfoLineCode alert={alert} />
|
<AlertInfoLineCode alert={alert} />
|
||||||
<AlertInfoLineDistance alert={alert} />
|
{!isSent && <AlertInfoLineDistance alert={alert} />}
|
||||||
<AlertInfoLineCreatedTime alert={alert} />
|
<AlertInfoLineCreatedTime alert={alert} />
|
||||||
<AlertInfoLineClosedTime alert={alert} />
|
<AlertInfoLineClosedTime alert={alert} />
|
||||||
{(!isSent || !isOpen) && <AlertInfoLineSubject alert={alert} />}
|
{(!isSent || !isOpen) && <AlertInfoLineSubject alert={alert} />}
|
||||||
|
|
Loading…
Add table
Reference in a new issue