diff options
-rw-r--r-- | app/(tabs)/index.tsx | 115 | ||||
-rw-r--r-- | package-lock.json | 40 | ||||
-rw-r--r-- | package.json | 1 |
3 files changed, 137 insertions, 19 deletions
diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx index c631903..b1eac11 100644 --- a/app/(tabs)/index.tsx +++ b/app/(tabs)/index.tsx @@ -1,16 +1,19 @@ 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 } from 'react'; -import AsyncStorage from '@react-native-async-storage/async-storage'; - +import React, { useState, useEffect, useRef } from 'react'; +import AsyncStorage from '@react-native-async-storage/async-storage'; +import MapView, { Marker } from 'react-native-maps'; export default function HomeScreen() { - const [username, setUsername] = useState(''); + 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 mapRef = useRef(null); const storeToken = async (token: string) => { if (Platform.OS === 'web') { @@ -20,9 +23,17 @@ export default function HomeScreen() { } }; + const storeUserId = async (userId: string) => { + if (Platform.OS === 'web') { + localStorage.setItem('userId', userId); + } else { + await AsyncStorage.setItem('userId', userId); + } + }; + const handleLogin = async () => { - if (!username || !password) { - Alert.alert('Error', 'Username and password are required.'); + if (!email || !password) { + Alert.alert('Error', 'Email and password are required.'); return; } @@ -38,11 +49,12 @@ export default function HomeScreen() { login(input: $input) { accessToken tokenType + userId } }`, variables: { input: { - email: username, + email: email, password, }, }, @@ -55,9 +67,11 @@ export default function HomeScreen() { const errorMessages = data.errors.map((error: any) => error.message); Alert.alert('Error', errorMessages.join('\n')); } else { - const { accessToken } = data.data.login; + const { accessToken, userId } = data.data.login; await storeToken(accessToken); + await storeUserId(String(userId)); setToken(accessToken); + setUserId(String(userId)); } } catch (err) { console.error('Login Error:', err); @@ -73,8 +87,42 @@ export default function HomeScreen() { const removeToken = async () => { if (Platform.OS === 'web') { localStorage.removeItem('token'); + localStorage.removeItem('userId'); } else { await AsyncStorage.removeItem('token'); + await AsyncStorage.removeItem('userId'); + } + }; + + const fetchMapData = async () => { + 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', + }, + 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, + }); + } + } catch (err) { + console.error('Fetch Map Data Error:', err); } }; @@ -82,19 +130,46 @@ export default function HomeScreen() { 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 || ''); }; retrieveToken(); }, []); + useEffect(() => { + if (token && userId) { + const intervalId = setInterval(fetchMapData, 10000); // Fetch map data every 10 seconds + + return () => clearInterval(intervalId); + } + }, [token, userId]); + + useEffect(() => { + if (mapRef.current && region) { + mapRef.current.animateToRegion(region, 1000); // Smoothly animate to the new region + } + }, [region]); + return ( <ParallaxScrollView> - {token ? ( - <ThemedView> - <Pressable onPress={handleLogout} style={styles.formButton}> - <Text style={{ color: 'white', textAlign: 'center' }}>Logout</Text> - </Pressable> - </ThemedView> + {token && userId ? ( + <> + <ThemedView> + <Pressable onPress={handleLogout} style={styles.formButton}> + <Text style={{ color: 'white', textAlign: 'center' }}>Logout</Text> + </Pressable> + </ThemedView> + <View> + <MapView + ref={mapRef} + initialRegion={region} + style={styles.map} + > + <Marker coordinate={coordinates} title="Start Point" /> + </MapView> + </View> + </> ) : ( <> <ThemedView style={styles.titleContainer}> @@ -102,11 +177,11 @@ export default function HomeScreen() { </ThemedView> <ThemedView style={styles.formContainer}> <View> - <ThemedText style={styles.text}>Username</ThemedText> + <ThemedText style={styles.text}>Email</ThemedText> <TextInput style={styles.formInput} - onChangeText={setUsername} - value={username} + onChangeText={setEmail} + value={email} /> </View> <View> @@ -166,6 +241,8 @@ const styles = StyleSheet.create({ textAlign: 'center', borderRadius: 8, color: 'white', + }, + map: { + height: 400, } }); - diff --git a/package-lock.json b/package-lock.json index 210cbfc..442b266 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "react-dom": "18.2.0", "react-native": "0.74.5", "react-native-gesture-handler": "~2.16.1", + "react-native-maps": "^1.18.0", "react-native-reanimated": "~3.10.1", "react-native-safe-area-context": "4.10.5", "react-native-screens": "3.31.1", @@ -6656,6 +6657,11 @@ "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==" }, + "node_modules/@types/geojson": { + "version": "7946.0.14", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", + "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==" + }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -15720,6 +15726,27 @@ "react": "^16.6.0 || ^17.0.0 || ^18.0.0" } }, + "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==", + "dependencies": { + "@types/geojson": "^7946.0.13" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": ">= 17.0.1", + "react-native": ">= 0.64.3", + "react-native-web": ">= 0.11" + }, + "peerDependenciesMeta": { + "react-native-web": { + "optional": true + } + } + }, "node_modules/react-native-reanimated": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.10.1.tgz", @@ -23068,6 +23095,11 @@ "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==" }, + "@types/geojson": { + "version": "7946.0.14", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", + "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==" + }, "@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -29809,6 +29841,14 @@ "shallowequal": "^1.1.0" } }, + "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==", + "requires": { + "@types/geojson": "^7946.0.13" + } + }, "react-native-reanimated": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.10.1.tgz", diff --git a/package.json b/package.json index b366c93..313993c 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "react-dom": "18.2.0", "react-native": "0.74.5", "react-native-gesture-handler": "~2.16.1", + "react-native-maps": "^1.18.0", "react-native-reanimated": "~3.10.1", "react-native-safe-area-context": "4.10.5", "react-native-screens": "3.31.1", |