diff options
author | Santo Cariotti <santo@dcariotti.me> | 2024-09-09 16:35:28 +0200 |
---|---|---|
committer | Santo Cariotti <santo@dcariotti.me> | 2024-09-09 16:35:28 +0200 |
commit | 558d10b39ceb6380272047da742c6d6b62830008 (patch) | |
tree | 5d3de5f46a34885ec351b1b13b774f346c5a5faf | |
parent | d14ce814d083e2ee61f29a235f60d936a545ab68 (diff) |
Add location update
-rw-r--r-- | app.json | 14 | ||||
-rw-r--r-- | app/(tabs)/index.tsx | 411 | ||||
-rw-r--r-- | package-lock.json | 72 | ||||
-rw-r--r-- | package.json | 7 | ||||
-rw-r--r-- | pnpm-lock.yaml | 117 |
5 files changed, 434 insertions, 187 deletions
@@ -13,7 +13,12 @@ "backgroundColor": "#ffffff" }, "ios": { - "supportsTablet": true + "supportsTablet": true, + "infoPlist": { + "NSLocationWhenInUseUsageDescription": "This app needs access to your location.", + "NSLocationAlwaysUsageDescription": "This app needs access to your location even when in the background.", + "UIBackgroundModes": ["location"] + } }, "android": { "adaptiveIcon": { @@ -21,7 +26,12 @@ "backgroundColor": "#ffffff" }, "googleServicesFile": "./google-services.json", - "package": "com.unibo.cas4" + "package": "com.unibo.cas4", + "permissions": [ + "ACCESS_FINE_LOCATION", + "ACCESS_COARSE_LOCATION", + "ACCESS_BACKGROUND_LOCATION" + ] }, "web": { "bundler": "metro", diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx index 237a032..b5bddcc 100644 --- a/app/(tabs)/index.tsx +++ b/app/(tabs)/index.tsx @@ -1,14 +1,38 @@ -import { Alert, Platform, Pressable, StyleSheet, Text, TextInput, View } from 'react-native'; -import ParallaxScrollView from '@/components/ParallaxScrollView'; -import { ThemedText } from '@/components/ThemedText'; -import { ThemedView } from '@/components/ThemedView'; -import React, { useState, useEffect, useRef } from 'react'; -import AsyncStorage from '@react-native-async-storage/async-storage'; -import MapView, { Marker } from 'react-native-maps'; -import * as Notifications from 'expo-notifications'; -import * as Device from 'expo-device'; -import Constants from 'expo-constants'; -import { Link } from 'expo-router'; +import { + Alert, + Platform, + Pressable, + StyleSheet, + Text, + TextInput, + View, +} from "react-native"; +import ParallaxScrollView from "@/components/ParallaxScrollView"; +import { ThemedText } from "@/components/ThemedText"; +import { ThemedView } from "@/components/ThemedView"; +import React, { useState, useEffect, useRef } from "react"; +import AsyncStorage from "@react-native-async-storage/async-storage"; +import MapView, { LatLng, Marker } from "react-native-maps"; +import * as Notifications from "expo-notifications"; +import * as Device from "expo-device"; +import * as Location from "expo-location"; +import Constants from "expo-constants"; +import { Link } from "expo-router"; +import * as TaskManager from "expo-task-manager"; + +const LOCATION_TASK_NAME = "background-location-task"; + +TaskManager.defineTask(LOCATION_TASK_NAME, async ({ data, error }) => { + if (error) { + console.error(error); + return; + } + if (data) { + const { locations } = data; + console.log("Received new locations:", locations); + // Process the locations here + } +}); interface NotificationData { id: string; @@ -23,36 +47,39 @@ Notifications.setNotificationHandler({ }), }); - function handleRegistrationError(errorMessage: string) { Alert.alert("Error registering this device", errorMessage); } async function registerForPushNotificationsAsync() { - if (Platform.OS === 'android') { - Notifications.setNotificationChannelAsync('default', { - name: 'default', + if (Platform.OS === "android") { + Notifications.setNotificationChannelAsync("default", { + name: "default", importance: Notifications.AndroidImportance.MAX, vibrationPattern: [0, 250, 250, 250], - lightColor: '#007AFF', + lightColor: "#007AFF", }); } if (Device.isDevice) { - const { status: existingStatus } = await Notifications.getPermissionsAsync(); + const { status: existingStatus } = + await Notifications.getPermissionsAsync(); let finalStatus = existingStatus; - if (existingStatus !== 'granted') { + if (existingStatus !== "granted") { const { status } = await Notifications.requestPermissionsAsync(); finalStatus = status; } - if (finalStatus !== 'granted') { - handleRegistrationError('Permission not granted to get push token for push notification!'); + if (finalStatus !== "granted") { + handleRegistrationError( + "Permission not granted to get push token for push notification!", + ); return; } const projectId = - Constants?.expoConfig?.extra?.eas?.projectId ?? Constants?.easConfig?.projectId; + Constants?.expoConfig?.extra?.eas?.projectId ?? + Constants?.easConfig?.projectId; if (!projectId) { - handleRegistrationError('Project ID not found'); + handleRegistrationError("Project ID not found"); } try { const pushTokenString = ( @@ -65,71 +92,84 @@ async function registerForPushNotificationsAsync() { handleRegistrationError(`${e}`); } } else { - handleRegistrationError('Must use physical device for push notifications'); + handleRegistrationError("Must use physical device for push notifications"); } } export default function HomeScreen() { - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const [token, setToken] = useState(''); - const [userId, setUserId] = useState(''); - const [coordinates, setCoordinates] = useState({ latitude: 44.49738301084014, longitude: 11.356121722966094 }); - const [region, setRegion] = useState({ latitude: 44.49738301084014, longitude: 11.356121722966094, latitudeDelta: 0.03, longitudeDelta: 0.03 }); - const [notification, setNotification] = useState<NotificationData|null>(null); + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [token, setToken] = useState(""); + const [userId, setUserId] = useState(""); + const [coordinates, setCoordinates] = useState({ + latitude: 0, + longitude: 0, + }); + const [region, setRegion] = useState({ + latitude: 0, + longitude: 0, + latitudeDelta: 0.03, + longitudeDelta: 0.03, + }); + const [notification, setNotification] = useState<NotificationData | null>( + null, + ); const mapRef = useRef(null); const storeToken = async (token: string) => { - if (Platform.OS === 'web') { - localStorage.setItem('token', token); + if (Platform.OS === "web") { + localStorage.setItem("token", token); } else { - await AsyncStorage.setItem('token', token); + await AsyncStorage.setItem("token", token); } }; const storeUserId = async (userId: string) => { - if (Platform.OS === 'web') { - localStorage.setItem('userId', userId); + if (Platform.OS === "web") { + localStorage.setItem("userId", userId); } else { - await AsyncStorage.setItem('userId', userId); + await AsyncStorage.setItem("userId", userId); } }; const handleLogin = async () => { if (!email || !password) { - Alert.alert('Error', 'Email and password are required.'); + Alert.alert("Error", "Email and password are required."); return; } try { - const response = await fetch(`${process.env.EXPO_PUBLIC_API_URL}/graphql`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - query: ` - mutation Login($input: LoginCredentials!) { - login(input: $input) { - accessToken - tokenType + const response = await fetch( + `${process.env.EXPO_PUBLIC_API_URL}/graphql`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + query: ` + mutation Login($input: LoginCredentials!) { + login(input: $input) { + accessToken + tokenType userId - } + } }`, - variables: { - input: { - email: email, - password, + variables: { + input: { + email: email, + password, + }, }, - }, - }), - }); + }), + }, + ); const data = await response.json(); if (data.errors) { const errorMessages = data.errors.map((error: any) => error.message); - Alert.alert('Error', errorMessages.join('\n')); + Alert.alert("Error", errorMessages.join("\n")); } else { const { accessToken, userId } = data.data.login; await storeToken(accessToken); @@ -138,40 +178,40 @@ export default function HomeScreen() { setUserId(String(userId)); registerForPushNotificationsAsync() - .then(async notificationToken => { + .then(async (notificationToken) => { if (!notificationToken) return; const regex = /ExponentPushToken\[(.*?)\]/; const match = notificationToken.match(regex); if (match && match[1]) { - notificationToken = match[1]; + notificationToken = match[1]; } - await fetch(`${process.env.EXPO_PUBLIC_API_URL}/graphql`, { - method: 'POST', - headers: { - 'Authorization': `Bearer ${accessToken}`, - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - query: ` + await fetch(`${process.env.EXPO_PUBLIC_API_URL}/graphql`, { + method: "POST", + headers: { + Authorization: `Bearer ${accessToken}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + query: ` mutation RegisterDevice($input: RegisterNotificationToken!) { registerDevice(input: $input) { id name email } } `, - variables: { - input: { - token: notificationToken, - }, + variables: { + input: { + token: notificationToken, }, - }), - }) + }, + }), + }); }) .catch((error: any) => alert(`${error}`)); } } catch (err) { - console.error('Login Error:', err); - Alert.alert('Error', 'An error occurred during login.'); + console.error("Login Error:", err); + Alert.alert("Error", "An error occurred during login."); } }; @@ -180,53 +220,74 @@ export default function HomeScreen() { }; const removeToken = async () => { - if (Platform.OS === 'web') { - localStorage.removeItem('token'); - localStorage.removeItem('userId'); + if (Platform.OS === "web") { + localStorage.removeItem("token"); + localStorage.removeItem("userId"); } else { - await AsyncStorage.removeItem('token'); - await AsyncStorage.removeItem('userId'); + await AsyncStorage.removeItem("token"); + await AsyncStorage.removeItem("userId"); } - setToken(''); - setUserId(''); + setToken(""); + setUserId(""); + }; + + const formatDate = (timestamp: string) => { + const date = new Date(parseInt(timestamp) * 1000); + return `${date.toDateString()} ${date.getHours()}:${(date.getMinutes() < 10 ? "0" : "") + date.getMinutes()}`; }; - const fetchMapData = async () => { + const updateLocation = async (coords: LatLng) => { + setCoordinates({ + latitude: coords.latitude, + longitude: coords.longitude, + }); + + if (region.longitude == 0 && region.latitude == 0) { + setRegion({ + latitude: coords.latitude, + longitude: coords.longitude, + latitudeDelta: 0.03, + longitudeDelta: 0.03, + }); + } + + if (!token || !userId) return; try { - const response = await fetch(`${process.env.EXPO_PUBLIC_API_URL}/graphql`, { - method: 'POST', - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json', + const response = await fetch( + `${process.env.EXPO_PUBLIC_API_URL}/graphql`, + { + method: "POST", + headers: { + Authorization: `Bearer ${token}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + query: ` + mutation NewPosition($input: PositionInput!) { + newPosition(input: $input) { + id userId createdAt latitude longitude movingActivity + } + } + `, + variables: { + input: { + latitude: coords.latitude, + longitude: coords.longitude, + movingActivity: 'STILL', + }, + }, + }), }, - body: JSON.stringify({ - query: `{ positions(userId: ${userId}) { id, userId, createdAt, latitude, longitude } }`, - }), - }); - + ); const data = await response.json(); - - if (data.data.positions && data.data.positions.length > 0) { - const position = data.data.positions[0]; - setCoordinates({ latitude: position.latitude, longitude: position.longitude }); - setRegion({ - latitude: position.latitude, - longitude: position.longitude, - latitudeDelta: 0.03, - longitudeDelta: 0.03, - }); - } + console.log(data) } catch (err) { - console.error('Fetch Map Data Error:', err); + console.error("Error on updating position"); } - }; - const formatDate = (timestamp: string) => { - const date = new Date(parseInt(timestamp) * 1000); - return `${date.toDateString()} ${date.getHours()}:${(date.getMinutes() < 10 ? '0' : '') + date.getMinutes()}`; - }; + } useEffect(() => { const fetchNotifications = async () => { @@ -236,15 +297,15 @@ export default function HomeScreen() { const response = await fetch( `${process.env.EXPO_PUBLIC_API_URL}/graphql`, { - method: 'POST', + method: "POST", headers: { Authorization: `Bearer ${token}`, - 'Content-Type': 'application/json', + "Content-Type": "application/json", }, body: JSON.stringify({ query: `{ notifications(seen: false) { id, createdAt } }`, }), - } + }, ); const data = await response.json(); @@ -253,45 +314,75 @@ export default function HomeScreen() { setNotification(data.data.notifications[0]); } } catch (err) { - console.error('Fetch notifications:', err); + console.error("Fetch notifications:", err); } }; if (token && userId) { - const intervalId = setInterval(fetchNotifications, 2000); + const intervalId = setInterval(fetchNotifications, 10000); return () => clearInterval(intervalId); } else { - setNotification(''); + setNotification(""); } }, [token, userId]); - useEffect(() => { const retrieveToken = async () => { - const storedToken = Platform.OS === 'web' ? localStorage.getItem('token') : await AsyncStorage.getItem('token'); - setToken(storedToken || ''); - const storedUserId = Platform.OS === 'web' ? localStorage.getItem('userId') : await AsyncStorage.getItem('userId'); - setUserId(storedUserId || ''); + const storedToken = + Platform.OS === "web" + ? localStorage.getItem("token") + : await AsyncStorage.getItem("token"); + setToken(storedToken || ""); + const storedUserId = + Platform.OS === "web" + ? localStorage.getItem("userId") + : await AsyncStorage.getItem("userId"); + setUserId(storedUserId || ""); }; retrieveToken(); }, []); useEffect(() => { - if (token && userId) { - const intervalId = setInterval(fetchMapData, 100000); - - return () => clearInterval(intervalId); - } - }, [token, userId]); - - useEffect(() => { if (mapRef.current && region) { mapRef.current.animateToRegion(region, 1000); } }, [region]); + useEffect(() => { + const startBackgroundLocationTracking = async () => { + try { + const { status } = await Location.requestForegroundPermissionsAsync(); + if (status === "granted") { + setInterval(async () => { + const location = await Location.getCurrentPositionAsync({}); + updateLocation(location.coords); + }, 2000); + + await Location.startLocationUpdatesAsync(LOCATION_TASK_NAME, { + accuracy: Location.Accuracy.Balanced, + distanceInterval: 0, + deferredUpdatesInterval: 1000, + showsBackgroundLocationIndicator: true, + foregroundService: { + notificationTitle: "CAS4 Location Tracking", + notificationBody: + "Your location is being tracked in the background.", + notificationColor: "#FFFFFF", + }, + }); + } else { + Alert.alert("Background location permission not granted"); + } + } catch (error) { + console.error("Error starting background location updates:", error); + } + }; + + startBackgroundLocationTracking(); + }, []); + return ( <ParallaxScrollView token={token} userId={userId}> {token && userId ? ( @@ -300,29 +391,31 @@ export default function HomeScreen() { <View> <Link href={`/notifications/${notification.id}`} - style={{ width: '100%' }} + style={{ width: "100%" }} > - <View style={styles.notificationBox}> - <Text style={styles.notificationBoxText}>Oh no, you are (or have been) in an alerted area in {formatDate(notification.createdAt)}!</Text> - <Text style={styles.notificationBoxText}>Click this banner to know more!</Text> - </View> + <View style={styles.notificationBox}> + <Text style={styles.notificationBoxText}> + Oh no, you are (or have been) in an alerted area in{" "} + {formatDate(notification.createdAt)}! + </Text> + <Text style={styles.notificationBoxText}> + Click this banner to know more! + </Text> + </View> </Link> </View> ) : ( - <> - </> + <></> )} <ThemedView> <Pressable onPress={handleLogout} style={styles.formButton}> - <Text style={{ color: 'white', textAlign: 'center' }}>Logout</Text> + <Text style={{ color: "white", textAlign: "center" }}> + Logout + </Text> </Pressable> </ThemedView> <View> - <MapView - ref={mapRef} - initialRegion={region} - style={styles.map} - > + <MapView ref={mapRef} initialRegion={region} style={styles.map}> <Marker coordinate={coordinates} title="Me" /> </MapView> </View> @@ -352,7 +445,9 @@ export default function HomeScreen() { </View> <View style={styles.buttonContainer}> <Pressable onPress={handleLogin} style={styles.formButton}> - <Text style={{ color: 'white', textAlign: 'center' }}>Login</Text> + <Text style={{ color: "white", textAlign: "center" }}> + Login + </Text> </Pressable> </View> </ThemedView> @@ -364,8 +459,8 @@ export default function HomeScreen() { const styles = StyleSheet.create({ titleContainer: { - flexDirection: 'row', - alignItems: 'center', + flexDirection: "row", + alignItems: "center", marginBottom: 20, }, text: { @@ -376,13 +471,13 @@ const styles = StyleSheet.create({ paddingHorizontal: 20, }, formInput: { - width: '100%', + width: "100%", paddingVertical: 12, paddingHorizontal: 16, borderRadius: 8, borderWidth: 1, - borderColor: '#ccc', - backgroundColor: '#f9f9f9', + borderColor: "#ccc", + backgroundColor: "#f9f9f9", marginBottom: 20, }, buttonContainer: { @@ -391,12 +486,12 @@ const styles = StyleSheet.create({ formButton: { paddingVertical: 12, paddingHorizontal: 24, - backgroundColor: '#007AFF', + backgroundColor: "#007AFF", fontSize: 16, - fontWeight: '600', - textAlign: 'center', + fontWeight: "600", + textAlign: "center", borderRadius: 8, - color: 'white', + color: "white", }, map: { height: 400, @@ -404,15 +499,15 @@ const styles = StyleSheet.create({ notificationBox: { padding: 40, borderRadius: 8, - shadowColor: '#000', + shadowColor: "#000", shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 4, - backgroundColor: '#EA2027', + backgroundColor: "#EA2027", }, notificationBoxText: { - color: '#fff', - textAlign: 'center', - fontWeight: 'bold' - } + color: "#fff", + textAlign: "center", + fontWeight: "bold", + }, }); diff --git a/package-lock.json b/package-lock.json index 9b34af1..be7528b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,21 +1,24 @@ { "name": "cas4", - "version": "1.0.0", + "version": "0.2.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "cas4", - "version": "1.0.0", + "version": "0.2.0", "dependencies": { + "@babel/runtime": "^7.25.6", "@expo/vector-icons": "^14.0.2", "@react-native-async-storage/async-storage": "1.23.1", + "@react-native/assets-registry": "^0.75.2", "@react-navigation/native": "^6.0.2", "expo": "~51.0.28", "expo-constants": "~16.0.2", "expo-device": "~6.0.2", "expo-font": "~12.0.9", "expo-linking": "~6.3.1", + "expo-location": "~17.0.1", "expo-notifications": "~0.28.16", "expo-router": "~3.5.23", "expo-splash-screen": "~0.27.5", @@ -26,7 +29,7 @@ "react-dom": "18.2.0", "react-native": "0.74.5", "react-native-gesture-handler": "~2.16.1", - "react-native-maps": "^1.14.0", + "react-native-maps": "1.14.0", "react-native-reanimated": "~3.10.1", "react-native-safe-area-context": "4.10.5", "react-native-screens": "3.31.1", @@ -2082,9 +2085,9 @@ "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" }, "node_modules/@babel/runtime": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.4.tgz", - "integrity": "sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz", + "integrity": "sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -5891,9 +5894,9 @@ } }, "node_modules/@react-native/assets-registry": { - "version": "0.74.87", - "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.74.87.tgz", - "integrity": "sha512-1XmRhqQchN+pXPKEKYdpJlwESxVomJOxtEnIkbo7GAlaN2sym84fHEGDXAjLilih5GVPpcpSmFzTy8jx3LtaFg==", + "version": "0.75.2", + "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.75.2.tgz", + "integrity": "sha512-P1dLHjpUeC0AIkDHRYcx0qLMr+p92IPWL3pmczzo6T76Qa9XzruQOYy0jittxyBK91Csn6HHQ/eit8TeXW8MVw==", "engines": { "node": ">=18" } @@ -9432,6 +9435,14 @@ "invariant": "^2.2.4" } }, + "node_modules/expo-location": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/expo-location/-/expo-location-17.0.1.tgz", + "integrity": "sha512-m+OzotzlAXO3ZZ1uqW5GC25nXW868zN+ROyBA1V4VF6jGay1ZEs4URPglCVUDzZby2F5wt24cMzqDKw2IX6nRw==", + "peerDependencies": { + "expo": "*" + } + }, "node_modules/expo-modules-autolinking": { "version": "1.11.2", "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-1.11.2.tgz", @@ -15873,9 +15884,9 @@ } }, "node_modules/react-native-maps": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/react-native-maps/-/react-native-maps-1.18.0.tgz", - "integrity": "sha512-S17nYUqeMptgIPaAZuVRo+eRelPreBBYQWw6jsxU7qQ12p+THSfFaqabcNn7fBmsXhT3T27iIl8ek8v1H8BaGw==", + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/react-native-maps/-/react-native-maps-1.14.0.tgz", + "integrity": "sha512-ai7h4UdRLGPFCguz1fI8n4sKLEh35nZXHAH4nSWyAeHGrN8K9GjICu9Xd4Q5Ok4h+WwrM6Xz5pGbF3Qm1tO6iQ==", "dependencies": { "@types/geojson": "^7946.0.13" }, @@ -15974,6 +15985,14 @@ "node": ">= 10.14.2" } }, + "node_modules/react-native/node_modules/@react-native/assets-registry": { + "version": "0.74.87", + "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.74.87.tgz", + "integrity": "sha512-1XmRhqQchN+pXPKEKYdpJlwESxVomJOxtEnIkbo7GAlaN2sym84fHEGDXAjLilih5GVPpcpSmFzTy8jx3LtaFg==", + "engines": { + "node": ">=18" + } + }, "node_modules/react-native/node_modules/@react-native/normalize-colors": { "version": "0.74.87", "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.74.87.tgz", @@ -19822,9 +19841,9 @@ "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" }, "@babel/runtime": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.4.tgz", - "integrity": "sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz", + "integrity": "sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==", "requires": { "regenerator-runtime": "^0.14.0" } @@ -22654,9 +22673,9 @@ } }, "@react-native/assets-registry": { - "version": "0.74.87", - "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.74.87.tgz", - "integrity": "sha512-1XmRhqQchN+pXPKEKYdpJlwESxVomJOxtEnIkbo7GAlaN2sym84fHEGDXAjLilih5GVPpcpSmFzTy8jx3LtaFg==" + "version": "0.75.2", + "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.75.2.tgz", + "integrity": "sha512-P1dLHjpUeC0AIkDHRYcx0qLMr+p92IPWL3pmczzo6T76Qa9XzruQOYy0jittxyBK91Csn6HHQ/eit8TeXW8MVw==" }, "@react-native/babel-plugin-codegen": { "version": "0.74.87", @@ -25334,6 +25353,12 @@ "invariant": "^2.2.4" } }, + "expo-location": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/expo-location/-/expo-location-17.0.1.tgz", + "integrity": "sha512-m+OzotzlAXO3ZZ1uqW5GC25nXW868zN+ROyBA1V4VF6jGay1ZEs4URPglCVUDzZby2F5wt24cMzqDKw2IX6nRw==", + "requires": {} + }, "expo-modules-autolinking": { "version": "1.11.2", "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-1.11.2.tgz", @@ -29965,6 +29990,11 @@ "chalk": "^4.0.0" } }, + "@react-native/assets-registry": { + "version": "0.74.87", + "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.74.87.tgz", + "integrity": "sha512-1XmRhqQchN+pXPKEKYdpJlwESxVomJOxtEnIkbo7GAlaN2sym84fHEGDXAjLilih5GVPpcpSmFzTy8jx3LtaFg==" + }, "@react-native/normalize-colors": { "version": "0.74.87", "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.74.87.tgz", @@ -30091,9 +30121,9 @@ } }, "react-native-maps": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/react-native-maps/-/react-native-maps-1.18.0.tgz", - "integrity": "sha512-S17nYUqeMptgIPaAZuVRo+eRelPreBBYQWw6jsxU7qQ12p+THSfFaqabcNn7fBmsXhT3T27iIl8ek8v1H8BaGw==", + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/react-native-maps/-/react-native-maps-1.14.0.tgz", + "integrity": "sha512-ai7h4UdRLGPFCguz1fI8n4sKLEh35nZXHAH4nSWyAeHGrN8K9GjICu9Xd4Q5Ok4h+WwrM6Xz5pGbF3Qm1tO6iQ==", "requires": { "@types/geojson": "^7946.0.13" } diff --git a/package.json b/package.json index 827734e..cb878e3 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,8 @@ "scripts": { "start": "expo start", "reset-project": "node ./scripts/reset-project.js", - "android": "expo start --android", - "ios": "expo start --ios", + "android": "expo run:android", + "ios": "expo run:ios", "web": "expo start --web", "test": "jest --watchAll", "lint": "expo lint" @@ -20,16 +20,19 @@ "@react-native-async-storage/async-storage": "1.23.1", "@react-native/assets-registry": "^0.75.2", "@react-navigation/native": "^6.0.2", + "asyncToGenerator": "link:@babel/runtime/helpers/asyncToGenerator", "expo": "~51.0.28", "expo-constants": "~16.0.2", "expo-device": "~6.0.2", "expo-font": "~12.0.9", "expo-linking": "~6.3.1", + "expo-location": "~17.0.1", "expo-notifications": "~0.28.16", "expo-router": "~3.5.23", "expo-splash-screen": "~0.27.5", "expo-status-bar": "~1.12.1", "expo-system-ui": "~3.0.7", + "expo-task-manager": "^11.8.2", "expo-web-browser": "~13.0.3", "react": "18.2.0", "react-dom": "18.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4a566ec..ca0cf39 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,6 +23,9 @@ importers: '@react-navigation/native': specifier: ^6.0.2 version: 6.1.18(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0) + asyncToGenerator: + specifier: link:@babel/runtime/helpers/asyncToGenerator + version: link:@babel/runtime/helpers/asyncToGenerator expo: specifier: ~51.0.28 version: 51.0.32(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2)) @@ -38,6 +41,9 @@ importers: expo-linking: specifier: ~6.3.1 version: 6.3.1(expo@51.0.32(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))) + expo-location: + specifier: ~17.0.1 + version: 17.0.1(expo@51.0.32(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))) expo-notifications: specifier: ~0.28.16 version: 0.28.16(expo@51.0.32(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))) @@ -53,6 +59,9 @@ importers: expo-system-ui: specifier: ~3.0.7 version: 3.0.7(expo@51.0.32(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))) + expo-task-manager: + specifier: ^11.8.2 + version: 11.8.2(expo@51.0.32(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))) expo-web-browser: specifier: ~13.0.3 version: 13.0.3(expo@51.0.32(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))) @@ -133,6 +142,9 @@ packages: resolution: {integrity: sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==} engines: {node: '>=6.9.0'} + '@babel/generator@7.2.0': + resolution: {integrity: sha512-BA75MVfRlFQG2EZgFYIwyT1r6xSkwfP2bdkY/kLZusEYWiJs4xCowab/alaEaT0wSvmVuXGqiefeBlP+7V1yKg==} + '@babel/generator@7.25.6': resolution: {integrity: sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==} engines: {node: '>=6.9.0'} @@ -1062,6 +1074,10 @@ packages: resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/types@24.9.0': + resolution: {integrity: sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==} + engines: {node: '>= 6'} + '@jest/types@26.6.2': resolution: {integrity: sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==} engines: {node: '>= 10.14.2'} @@ -1379,6 +1395,9 @@ packages: '@types/istanbul-lib-report@3.0.3': resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} + '@types/istanbul-reports@1.1.2': + resolution: {integrity: sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==} + '@types/istanbul-reports@3.0.4': resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} @@ -1418,6 +1437,9 @@ packages: '@types/yargs-parser@21.0.3': resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + '@types/yargs@13.0.12': + resolution: {integrity: sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==} + '@types/yargs@15.0.19': resolution: {integrity: sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA==} @@ -1634,8 +1656,8 @@ packages: peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - babel-plugin-react-compiler@0.0.0: - resolution: {integrity: sha512-Kigl0V36a/6hLVH7+CCe1CCtU3mFBqBd829V//VtuG7I/pyq+B2QZJqOefd63snQmdfCryNhO9XW1FbGPBvYDA==} + babel-plugin-react-compiler@0.0.0-experimental-7449567-20240905: + resolution: {integrity: sha512-ltBywPFOEf1rRnkRQ1TiiPJeqJ1Cte86bo4tpSPsfqGTTsiyUo8OLyOR13EG08QIFTQd6HfGGgjpE9Kv/t5Vcg==} babel-plugin-react-native-web@0.19.12: resolution: {integrity: sha512-eYZ4+P6jNcB37lObWIg0pUbi7+3PKoU1Oie2j0C8UF3cXyXoR74tO2NBjI/FORb2LJyItJZEAmjU5pSaJYEL1w==} @@ -2304,6 +2326,11 @@ packages: expo-linking@6.3.1: resolution: {integrity: sha512-xuZCntSBGWCD/95iZ+mTUGTwHdy8Sx+immCqbUBxdvZ2TN61P02kKg7SaLS8A4a/hLrSCwrg5tMMwu5wfKr35g==} + expo-location@17.0.1: + resolution: {integrity: sha512-m+OzotzlAXO3ZZ1uqW5GC25nXW868zN+ROyBA1V4VF6jGay1ZEs4URPglCVUDzZby2F5wt24cMzqDKw2IX6nRw==} + peerDependencies: + expo: '*' + expo-modules-autolinking@1.11.2: resolution: {integrity: sha512-fdcaNO8ucHA3yLNY52ZUENBcAG7KEx8QyMmnVNavO1JVBGRMZG8JyVcbrhYQDtVtpxkbai5YzwvLutINvbDZDQ==} hasBin: true @@ -2349,6 +2376,11 @@ packages: peerDependencies: expo: '*' + expo-task-manager@11.8.2: + resolution: {integrity: sha512-Uhy3ol5gYeZOyeRFddYjLI1B2DGRH1gjp/YC8Hpn5p5MVENviySoKNF+wd98rRvOAokzrzElyDBHSTfX+C3tpg==} + peerDependencies: + expo: '*' + expo-web-browser@13.0.3: resolution: {integrity: sha512-HXb7y82ApVJtqk8tManyudtTrCtx8xcUnVzmJECeHCB0SsWSQ+penVLZxJkcyATWoJOsFMnfVSVdrTcpKKGszQ==} peerDependencies: @@ -3774,6 +3806,10 @@ packages: resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==} engines: {node: '>=6'} + pretty-format@24.9.0: + resolution: {integrity: sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==} + engines: {node: '>= 6'} + pretty-format@26.6.2: resolution: {integrity: sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==} engines: {node: '>= 10'} @@ -4483,6 +4519,10 @@ packages: resolution: {integrity: sha512-7bBrcF+/LQzSgFmT0X5YclVqQxtv7TDJ1f8Wj7ibBu/U6BMLeOpUxuZjV7rMc44UtKxlnMFigdhFAIszSX1DMg==} engines: {node: '>= 0.4'} + trim-right@1.0.1: + resolution: {integrity: sha512-WZGXGstmCWgeevgTL54hrCuw1dyMQIzWy7ZfqRJfSmJZBwklI15egmQytFP6bPidmw3M8d5yEowl1niq4vmqZw==} + engines: {node: '>=0.10.0'} + ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} @@ -4577,6 +4617,9 @@ packages: resolution: {integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==} engines: {node: '>=4'} + unimodules-app-loader@4.6.0: + resolution: {integrity: sha512-FRNIlx7sLBDVPG117JnEBhnzZkTIgZTEwYW2rzrY9HdvLBTpRN+k0dxY50U/CAhFHW3zMD0OP5JAlnSQRhx5HA==} + unique-filename@3.0.0: resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -4880,6 +4923,15 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + zod-validation-error@2.1.0: + resolution: {integrity: sha512-VJh93e2wb4c3tWtGgTa0OF/dTt/zoPCPzXq4V11ZjxmEAFaPi/Zss1xIZdEB5RD8GD00U0/iVXgqkF77RV7pdQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.18.0 + + zod@3.23.8: + resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + snapshots: '@ampproject/remapping@2.3.0': @@ -4918,6 +4970,14 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/generator@7.2.0': + dependencies: + '@babel/types': 7.25.6 + jsesc: 2.5.2 + lodash: 4.17.21 + source-map: 0.5.7 + trim-right: 1.0.1 + '@babel/generator@7.25.6': dependencies: '@babel/types': 7.25.6 @@ -6373,6 +6433,12 @@ snapshots: transitivePeerDependencies: - supports-color + '@jest/types@24.9.0': + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 1.1.2 + '@types/yargs': 13.0.12 + '@jest/types@26.6.2': dependencies: '@types/istanbul-lib-coverage': 2.0.6 @@ -6942,6 +7008,11 @@ snapshots: dependencies: '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports@1.1.2': + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-lib-report': 3.0.3 + '@types/istanbul-reports@3.0.4': dependencies: '@types/istanbul-lib-report': 3.0.3 @@ -6988,6 +7059,10 @@ snapshots: '@types/yargs-parser@21.0.3': {} + '@types/yargs@13.0.12': + dependencies: + '@types/yargs-parser': 21.0.3 + '@types/yargs@15.0.19': dependencies: '@types/yargs-parser': 21.0.3 @@ -7219,7 +7294,15 @@ snapshots: transitivePeerDependencies: - supports-color - babel-plugin-react-compiler@0.0.0: {} + babel-plugin-react-compiler@0.0.0-experimental-7449567-20240905: + dependencies: + '@babel/generator': 7.2.0 + '@babel/types': 7.25.6 + chalk: 4.1.2 + invariant: 2.2.4 + pretty-format: 24.9.0 + zod: 3.23.8 + zod-validation-error: 2.1.0(zod@3.23.8) babel-plugin-react-native-web@0.19.12: {} @@ -7257,7 +7340,7 @@ snapshots: '@babel/preset-react': 7.24.7(@babel/core@7.25.2) '@babel/preset-typescript': 7.24.7(@babel/core@7.25.2) '@react-native/babel-preset': 0.74.87(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2)) - babel-plugin-react-compiler: 0.0.0 + babel-plugin-react-compiler: 0.0.0-experimental-7449567-20240905 babel-plugin-react-native-web: 0.19.12 react-refresh: 0.14.2 transitivePeerDependencies: @@ -7945,6 +8028,10 @@ snapshots: - expo - supports-color + expo-location@17.0.1(expo@51.0.32(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))): + dependencies: + expo: 51.0.32(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2)) + expo-modules-autolinking@1.11.2: dependencies: chalk: 4.1.2 @@ -8020,6 +8107,11 @@ snapshots: transitivePeerDependencies: - supports-color + expo-task-manager@11.8.2(expo@51.0.32(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))): + dependencies: + expo: 51.0.32(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2)) + unimodules-app-loader: 4.6.0 + expo-web-browser@13.0.3(expo@51.0.32(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))): dependencies: expo: 51.0.32(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2)) @@ -9780,6 +9872,13 @@ snapshots: pretty-bytes@5.6.0: {} + pretty-format@24.9.0: + dependencies: + '@jest/types': 24.9.0 + ansi-regex: 4.1.1 + ansi-styles: 3.2.1 + react-is: 16.13.1 + pretty-format@26.6.2: dependencies: '@jest/types': 26.6.2 @@ -10578,6 +10677,8 @@ snapshots: typedarray.prototype.slice: 1.0.3 which-typed-array: 1.1.15 + trim-right@1.0.1: {} + ts-interface-checker@0.1.13: {} tslib@2.7.0: {} @@ -10670,6 +10771,8 @@ snapshots: unicode-property-aliases-ecmascript@2.1.0: {} + unimodules-app-loader@4.6.0: {} + unique-filename@3.0.0: dependencies: unique-slug: 4.0.0 @@ -10939,3 +11042,9 @@ snapshots: yargs-parser: 21.1.1 yocto-queue@0.1.0: {} + + zod-validation-error@2.1.0(zod@3.23.8): + dependencies: + zod: 3.23.8 + + zod@3.23.8: {} |