diff options
author | Santo Cariotti <santo@dcariotti.me> | 2024-08-31 12:37:42 +0200 |
---|---|---|
committer | Santo Cariotti <santo@dcariotti.me> | 2024-08-31 12:37:42 +0200 |
commit | 819db6470099f9833d5a5027020c369d4b40e323 (patch) | |
tree | 62f68555b5e07443466fbb7e27bb44ce0ad5efe9 /app | |
parent | 509785982a1b4ac9ab145bac90d97be7044228c4 (diff) |
Add boxs on alert page
Diffstat (limited to 'app')
-rw-r--r-- | app/(tabs)/_layout.tsx | 6 | ||||
-rw-r--r-- | app/(tabs)/alerts.tsx | 46 | ||||
-rw-r--r-- | app/(tabs)/alerts/[id].tsx | 65 | ||||
-rw-r--r-- | app/(tabs)/alerts/index.tsx | 197 | ||||
-rw-r--r-- | app/(tabs)/index.tsx | 6 |
5 files changed, 270 insertions, 50 deletions
diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx index 97e98de..a0a2bba 100644 --- a/app/(tabs)/_layout.tsx +++ b/app/(tabs)/_layout.tsx @@ -24,7 +24,7 @@ export default function TabLayout() { }} /> <Tabs.Screen - name="alerts" + name="alerts/index" options={{ title: 'Alerts', tabBarIcon: ({ color, focused }) => ( @@ -32,6 +32,10 @@ export default function TabLayout() { ), }} /> + <Tabs.Screen + name="alerts/[id]" + options={{href: null}} + /> </Tabs> ); } diff --git a/app/(tabs)/alerts.tsx b/app/(tabs)/alerts.tsx deleted file mode 100644 index 135771f..0000000 --- a/app/(tabs)/alerts.tsx +++ /dev/null @@ -1,46 +0,0 @@ -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 { useFocusEffect } from '@react-navigation/native'; -import { router } from 'expo-router'; - -export default function AlertsScreen() { - const [token, setToken] = useState(''); - const [userId, setUserId] = useState(''); - - const checkAuth = async () => { - const storedToken = Platform.OS === 'web' ? localStorage.getItem('token') : await AsyncStorage.getItem('token'); - const storedUserId = Platform.OS === 'web' ? localStorage.getItem('userId') : await AsyncStorage.getItem('userId'); - - setToken(storedToken || ''); - setUserId(storedUserId || ''); - - if (!storedToken || !storedUserId) { - Alert.alert('Login required', 'You must log in to the system if you want to see alerts list', [ - { - text: 'Ok', - onPress: () => router.push('/') - } - ]); - } - }; - - useFocusEffect( - React.useCallback(() => { - checkAuth(); - }, []) - ); - - - return ( - <ParallaxScrollView> - </ParallaxScrollView> - ); -} - -const styles = StyleSheet.create({ -}); diff --git a/app/(tabs)/alerts/[id].tsx b/app/(tabs)/alerts/[id].tsx new file mode 100644 index 0000000..cc425db --- /dev/null +++ b/app/(tabs)/alerts/[id].tsx @@ -0,0 +1,65 @@ +import { + Alert, + Platform, + StyleSheet, +} from 'react-native'; +import ParallaxScrollView from '@/components/ParallaxScrollView'; +import { ThemedText } from '@/components/ThemedText'; +import { ThemedView } from '@/components/ThemedView'; +import React, { useState, useCallback } from 'react'; +import AsyncStorage from '@react-native-async-storage/async-storage'; +import { useFocusEffect } from '@react-navigation/native'; +import { router, useLocalSearchParams } from 'expo-router'; + +export default function AlertIdScreen() { + const [token, setToken] = useState(''); + const [userId, setUserId] = useState(''); + + const checkAuth = async () => { + const storedToken = + Platform.OS === 'web' + ? localStorage.getItem('token') + : await AsyncStorage.getItem('token'); + const storedUserId = + Platform.OS === 'web' + ? localStorage.getItem('userId') + : await AsyncStorage.getItem('userId'); + + setToken(storedToken || ''); + setUserId(storedUserId || ''); + + if (!storedToken || !storedUserId) { + Alert.alert( + 'Login required', + 'You must log in to the system if you want to see alerts list', + [ + { + text: 'Ok', + onPress: () => router.push('/'), + }, + ] + ); + } + }; + + useFocusEffect( + useCallback(() => { + checkAuth(); + }, []) + ); + + + const { id } = useLocalSearchParams(); + + + return ( + <ParallaxScrollView> + <ThemedView> + <ThemedText>{ id }</ThemedText> + </ThemedView> + </ParallaxScrollView> + ); +} + +const styles = StyleSheet.create({ +}); diff --git a/app/(tabs)/alerts/index.tsx b/app/(tabs)/alerts/index.tsx new file mode 100644 index 0000000..b4aa18d --- /dev/null +++ b/app/(tabs)/alerts/index.tsx @@ -0,0 +1,197 @@ +import { + Alert, + FlatList, + Platform, + StyleSheet, + View, + RefreshControl, +} from 'react-native'; +import ParallaxScrollView from '@/components/ParallaxScrollView'; +import { ThemedText } from '@/components/ThemedText'; +import { ThemedView } from '@/components/ThemedView'; +import React, { useState, useCallback } from 'react'; +import AsyncStorage from '@react-native-async-storage/async-storage'; +import { useFocusEffect } from '@react-navigation/native'; +import { Link, router } from 'expo-router'; +import { Ionicons } from '@expo/vector-icons'; + +interface AlertData { + id: string; + userId: string; + createdAt: string; + area: string; + level: string; +} + +export default function AlertsScreen() { + const [token, setToken] = useState(''); + const [userId, setUserId] = useState(''); + const [alerts, setAlerts] = useState<AlertData[]>([]); + const [refreshing, setRefreshing] = useState(false); + + const fetchAlerts = 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: `{ alerts { id, userId, createdAt, area, level } }`, + }), + } + ); + + const data = await response.json(); + + if (data.errors) { + Alert.alert('Error', 'Error fetching data'); + } else if (data.data.alerts) { + console.log(`Found ${data.data.alerts.length} alerts`); + setAlerts(data.data.alerts); + } + } catch (err) { + console.error('Fetch Map Data Error:', err); + } + }; + + const checkAuth = async () => { + const storedToken = + Platform.OS === 'web' + ? localStorage.getItem('token') + : await AsyncStorage.getItem('token'); + const storedUserId = + Platform.OS === 'web' + ? localStorage.getItem('userId') + : await AsyncStorage.getItem('userId'); + + setToken(storedToken || ''); + setUserId(storedUserId || ''); + + if (!storedToken || !storedUserId) { + Alert.alert( + 'Login required', + 'You must log in to the system if you want to see alerts list', + [ + { + text: 'Ok', + onPress: () => router.push('/'), + }, + ] + ); + } + }; + + useFocusEffect( + useCallback(() => { + checkAuth(); + fetchAlerts(); + }, []) + ); + + const onRefresh = useCallback(() => { + setRefreshing(true); + fetchAlerts().finally(() => setRefreshing(false)); + }, []); + + const formatDate = (timestamp: string) => { + const date = new Date(parseInt(timestamp) * 1000); + return `${date.toDateString()} ${date.getHours()}:${(date.getMinutes() < 10 ? '0' : '') + date.getMinutes()}`; + }; + + const renderAlert = ({ item }: { item: AlertData }) => ( + <ThemedView style={styles.alertContainer}> + <View + style={[ + styles.alertBox, + { + backgroundColor: item.level === 'ONE' + ? '#27ae60' + : item.level === 'TWO' + ? '#e67e22' + : '#c0392b', + }, + ]} + > + <Link + href={`/alerts/${item.id}`} + style={{ width: '100%' }} + > + <View style={styles.dateRow}> + <Ionicons + name="calendar-outline" + size={18} + color="white" + style={styles.icon} + /> + <ThemedText style={styles.dateText}> + {formatDate(item.createdAt)} + </ThemedText> + </View> + </Link> + </View> + </ThemedView> + ); + + return ( + <FlatList + ListHeaderComponent={ + <ParallaxScrollView> + <ThemedView style={styles.header}> + <ThemedText type="subtitle">Alerts</ThemedText> + <ThemedText type="default"> + Click on an alert to show more info about the area. + </ThemedText> + </ThemedView> + </ParallaxScrollView> + } + data={alerts} + renderItem={renderAlert} + keyExtractor={(item) => item.id} + contentContainerStyle={styles.listContent} + refreshControl={ + <RefreshControl refreshing={refreshing} onRefresh={onRefresh} /> + } + /> + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + header: { + padding: 16, + backgroundColor: '#fff', + }, + alertContainer: { + paddingHorizontal: 16, + paddingBottom: 16, + }, + alertBox: { + padding: 16, + borderRadius: 8, + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.1, + shadowRadius: 4, + }, + dateRow: { + flexDirection: 'row', + alignItems: 'center', + }, + icon: { + marginRight: 8, + }, + dateText: { + color: '#fff', + }, + listContent: { + paddingBottom: 32, + }, +}); diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx index d8fcccd..48c47b4 100644 --- a/app/(tabs)/index.tsx +++ b/app/(tabs)/index.tsx @@ -139,7 +139,7 @@ export default function HomeScreen() { useEffect(() => { if (token && userId) { - const intervalId = setInterval(fetchMapData, 10000); // Fetch map data every 10 seconds + const intervalId = setInterval(fetchMapData, 10000); return () => clearInterval(intervalId); } @@ -147,7 +147,7 @@ export default function HomeScreen() { useEffect(() => { if (mapRef.current && region) { - mapRef.current.animateToRegion(region, 1000); // Smoothly animate to the new region + mapRef.current.animateToRegion(region, 1000); } }, [region]); @@ -173,7 +173,7 @@ export default function HomeScreen() { ) : ( <> <ThemedView style={styles.titleContainer}> - <ThemedText type="title">Welcome, mate!</ThemedText> + <ThemedText type="subtitle">Welcome, mate!</ThemedText> </ThemedView> <ThemedView style={styles.formContainer}> <View> |