From 0c274decbe0d0e03b2266b906641c13c46e5d0d6 Mon Sep 17 00:00:00 2001 From: Sumit Nalavade Date: Wed, 17 Jan 2024 03:28:31 -0600 Subject: [PATCH 1/2] Remove popups for certain requests --- app/components/MapView.tsx | 33 ++++---- app/components/sheets/RouteDetails.tsx | 42 +++++---- app/components/sheets/StopTimetable.tsx | 108 ++++++++++++------------ app/components/ui/FavoritePill.tsx | 14 +-- app/components/ui/StopCell.tsx | 20 +++-- app/components/ui/Timetable.tsx | 80 +++++++++++------- app/index.tsx | 35 ++++++-- 7 files changed, 198 insertions(+), 134 deletions(-) diff --git a/app/components/MapView.tsx b/app/components/MapView.tsx index 55efd76..fa8239d 100644 --- a/app/components/MapView.tsx +++ b/app/components/MapView.tsx @@ -40,7 +40,7 @@ const Map: React.FC = () => { const updateBuses = async () => { if (!selectedRoute || !authToken) return - let busesResponse: IGetVehiclesResponse + let busesResponse: IGetVehiclesResponse = [] try { busesResponse = await getVehicles([selectedRoute.key], authToken) as IGetVehiclesResponse; @@ -48,8 +48,7 @@ const Map: React.FC = () => { } catch (error) { console.error(error); // prevent alert loop from filling screen with alerts. - clearBusRefreshInterval() - throw new Error("Error while updating buses"); + clearBusRefreshInterval(); } if (busesResponse.length == 0 || !busesResponse[0]?.vehiclesByDirections) { @@ -66,7 +65,7 @@ const Map: React.FC = () => { setBuses(extracted) } - + function selectRoute(route: IMapRoute) { if (selectedRoute?.key === route.key) return; @@ -109,18 +108,18 @@ const Map: React.FC = () => { setZoomToStopLatLng((lat, lng) => { // Animate map to the current location const region = { - latitude: lat-.002, + latitude: lat - .002, longitude: lng, latitudeDelta: 0.005, longitudeDelta: 0.005 }; - + mapViewRef.current?.animateToRegion(region, 250); }) - + }, []); - + const centerViewOnRoutes = () => { let coords: LatLng[] = []; @@ -174,7 +173,7 @@ const Map: React.FC = () => { // Animate map to the current location const region = { - latitude: location.coords.latitude-.002, + latitude: location.coords.latitude - .002, longitude: location.coords.longitude, latitudeDelta: 0.01, longitudeDelta: 0.01 @@ -184,14 +183,14 @@ const Map: React.FC = () => { setIsViewCenteredOnUser(true); } - + return ( <> - setIsViewCenteredOnUser(false)} showsMyLocationButton={false} // we have our own > @@ -211,14 +210,14 @@ const Map: React.FC = () => { }) return ( - selectRoute(drawnRoute)}/> + selectRoute(drawnRoute)} /> ) })} {selectedRoute && selectedRoute?.patternPaths.flatMap((patternPath, index1) => ( patternPath.patternPoints.map((patternPoint, index2) => { const stop = patternPoint.stop - + if (stop) { const lineColor = selectedRoute?.directionList[0]?.lineColor ?? "#FFFF"; diff --git a/app/components/sheets/RouteDetails.tsx b/app/components/sheets/RouteDetails.tsx index 65b90f2..55ff85d 100644 --- a/app/components/sheets/RouteDetails.tsx +++ b/app/components/sheets/RouteDetails.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { View, Text, TouchableOpacity, NativeSyntheticEvent, Alert } from "react-native"; +import { View, Text, TouchableOpacity, NativeSyntheticEvent } from "react-native"; import { BottomSheetModal, BottomSheetView, BottomSheetFlatList } from "@gorhom/bottom-sheet"; import SegmentedControl, { NativeSegmentedControlIOSChangeEvent } from "@react-native-segmented-control/segmented-control"; import { Ionicons } from '@expo/vector-icons'; @@ -25,13 +25,15 @@ const RouteDetails: React.FC = ({ sheetRef }) => { const stopEstimates = useAppStore((state) => state.stopEstimates); const setStopEstimates = useAppStore(state => state.setStopEstimates); - + // Controls SegmentedControl const [selectedDirectionIndex, setSelectedDirectionIndex] = useState(0); const [processedStops, setProcessedStops] = useState([]); const [selectedRoute, setSelectedRoute] = useState(null); + const [error, setError] = useState(false); + // cleanup this view when the sheet is closed const closeModal = () => { sheetRef.current?.dismiss(); @@ -88,19 +90,24 @@ const RouteDetails: React.FC = ({ sheetRef }) => { const newStopEstimates: ICachedStopEstimate[] = []; // load stop estimates concurrently - const promises = allStops.map(stop => - getNextDepartureTimes(currentSelectedRoute.key, directionKeys, stop.stopCode, authToken) - .then(response => { - GetNextDepartTimesResponseSchema.parse(response); - - newStopEstimates.push({ stopCode: stop.stopCode, departureTimes: response }); - }) - .catch(error => { - console.error(error); - - Alert.alert("Something went wrong", "Some features may not work correctly. Please try again later."); - }) - ); + const promises = allStops.map(async stop => { + try { + const response = await getNextDepartureTimes(currentSelectedRoute.key, directionKeys, stop.stopCode, authToken); + GetNextDepartTimesResponseSchema.parse(response); + + newStopEstimates.push({ stopCode: stop.stopCode, departureTimes: response }); + } catch (error) { + console.error(error); + + setError(true); + + // Make sure to return as if we don't the error state will be reset by the next line + return; + } + + // If we rerun the request and there is no error, make sure to reset the error state + setError(false); + }); await Promise.all(promises); setStopEstimates(newStopEstimates); @@ -112,6 +119,7 @@ const RouteDetails: React.FC = ({ sheetRef }) => { const snapPoints = ['25%', '45%', '85%']; + return ( = ({ sheetRef }) => { } + + { error && Something went wrong. Please try again later } - {selectedRoute && + {!error && selectedRoute && = ({ sheetRef }) => { const [nonRouteSchedules, setNonRouteSchedules] = useState(null); const [routeSchedules, setRouteSchedules] = useState(null); + const [error, setError] = useState(false); + async function loadSchedule(newSelectedStop: IStop | null = null) { if (!newSelectedStop || !authToken) return; @@ -54,7 +56,10 @@ const StopTimetable: React.FC = ({ sheetRef }) => { } catch (error) { console.error(error); - Alert.alert("Something went wrong", "Some features may not work correctly. Please try again later."); + setError(true); + + // Make sure to return as if we don't the error state will be reset by the next line + return; } } @@ -98,64 +103,63 @@ const StopTimetable: React.FC = ({ sheetRef }) => { - {!routeSchedules && } + {!routeSchedules && !error && } - {routeSchedules && ( - {routeSchedules && - index.toString()} - ItemSeparatorComponent={() => } - renderItem={({ item, index }) => { - return ( - - - - ) - }} - /> - } - - {showNonRouteSchedules && - - + { error && Something went wrong. Please try again later } + + {!error && routeSchedules && ( + + {routeSchedules && ( index.toString()} + data={routeSchedules} scrollEnabled={false} + keyExtractor={(_, index) => index.toString()} ItemSeparatorComponent={() => } - renderItem={({ item }) => { - - + renderItem={({ item, index }) => { return ( - - ) + + + + ); }} /> - - } - - {nonRouteSchedules && nonRouteSchedules.length > 0 && - // show other routes button - setShowNonRouteSchedules(!showNonRouteSchedules)} - > - {showNonRouteSchedules ? "Hide" : "Show"} Other Routes - - } - - )} + )} + + {showNonRouteSchedules && ( + + + index.toString()} + scrollEnabled={false} + ItemSeparatorComponent={() => } + renderItem={({ item }) => { + return ; + }} + /> + + )} + + {nonRouteSchedules && nonRouteSchedules.length > 0 && ( + // show other routes button + setShowNonRouteSchedules(!showNonRouteSchedules)} + > + {showNonRouteSchedules ? "Hide" : "Show"} Other Routes + + )} + + )} ) } diff --git a/app/components/ui/FavoritePill.tsx b/app/components/ui/FavoritePill.tsx index 3e24f46..167658b 100644 --- a/app/components/ui/FavoritePill.tsx +++ b/app/components/ui/FavoritePill.tsx @@ -1,7 +1,5 @@ - - import React, { memo, useEffect, useState } from 'react' -import { Alert, TouchableOpacity } from 'react-native'; +import { Text, TouchableOpacity, Alert } from 'react-native'; import AsyncStorage from '@react-native-async-storage/async-storage'; import {FontAwesome} from '@expo/vector-icons'; @@ -26,7 +24,9 @@ const FavoritePill: React.FC = ({ routeId }) => { } catch(error) { console.error(error); - Alert.alert("Something went wrong", "Some features may not work correctly. Please try again later."); + Alert.alert("Something Went Wrong", "Please try again later") + + return; } }, []) @@ -62,10 +62,12 @@ const FavoritePill: React.FC = ({ routeId }) => { } catch (error) { console.error(error); - Alert.alert("Something went wrong", "Some features may not work correctly. Please try again later."); + Alert.alert("Something Went Wrong", "Please try again later"); + + return; } } - + return ( {isFavorite ? diff --git a/app/components/ui/StopCell.tsx b/app/components/ui/StopCell.tsx index f0b49da..b31e7ab 100644 --- a/app/components/ui/StopCell.tsx +++ b/app/components/ui/StopCell.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect, useRef } from "react"; -import { View, Text, ActivityIndicator, FlatList, TouchableOpacity, Alert, AppState } from 'react-native'; +import { View, Text, ActivityIndicator, FlatList, TouchableOpacity, AppState } from 'react-native'; import { MaterialCommunityIcons } from '@expo/vector-icons'; import { GetNextDepartTimesResponseSchema, IAmenity, IRouteDirectionTime, IStop } from "../../../utils/interfaces"; import TimeBubble from "./TimeBubble"; @@ -31,6 +31,8 @@ const StopCell: React.FC = ({ stop, directionTimes, color, disabled, amen const stopEstimates = useAppStore((state) => state.stopEstimates); const updateStopEstimate = useAppStore(state => state.updateStopEstimate); + const [error, setError] = useState(false); + useEffect(() => { let totalDeviation = 0; @@ -96,8 +98,13 @@ const StopCell: React.FC = ({ stop, directionTimes, color, disabled, amen } catch (error) { console.error(error); - Alert.alert("Something went wrong", "Some features may not work correctly. Please try again later."); + setError(true); + + return; + } + + setError(false); }, 30000); }; @@ -137,14 +144,17 @@ const StopCell: React.FC = ({ stop, directionTimes, color, disabled, amen - {status == "Loading" ? + {error ? ( + Something went wrong. Please try again later + ) : status === "Loading" ? ( - : + ) : ( {status} - } + )} + = ({ item, tintColor, stopCode }) => { const authToken = useAppStore((state) => state.authToken); - const [estimate, setEstimate] = React.useState(null); - const [tableRows, setTableRows] = React.useState([]); + const [estimate, setEstimate] = useState(null); + const [tableRows, setTableRows] = useState([]); + + const [error, setError] = useState(false); useEffect(() => { - getStopEstimates(stopCode, moment().toDate(), authToken!) - .then((response) => { - try { - GetStopEstimatesResponseSchema.parse(response); - const estimate = response.routeStopSchedules.find((schedule) => schedule.directionName === item.directionName && schedule.routeName === item.routeName) - if (estimate) setEstimate(estimate); - } catch (error) { - console.error(error); - - Alert.alert("Something went wrong", "Some features may not work correctly. Please try again later."); + const fetchData = async () => { + try { + const response = await getStopEstimates(stopCode, moment().toDate(), authToken!); + GetStopEstimatesResponseSchema.parse(response); + + const estimate = response.routeStopSchedules.find((schedule) => schedule.directionName === item.directionName && schedule.routeName === item.routeName); + + if (estimate) { + setEstimate(estimate); } - }) - }, []) + } catch (error) { + console.error(error); + + setError(true); + + return; + } + + setError(false); + }; + + fetchData(); // Call the async function immediately + }, [stopCode, authToken, item.directionName, item.routeName]); useEffect(() => { const now = moment(); @@ -72,7 +84,7 @@ const Timetable: React.FC = ({ item, tintColor, stopCode }) => { foundNextStop = true; } } - + return { time: departTime.format("h:mm"), color: color, @@ -85,7 +97,7 @@ const Timetable: React.FC = ({ item, tintColor, stopCode }) => { let foundHighlight = false; // chunk into rows of 5 - for (let i = 0; i < processed.length; i += sliceLength) { + for (let i = 0; i < processed.length; i += sliceLength) { // check if any of the items in the row should be highlighted let shouldHighlight = processed.slice(i, i + sliceLength).some((item) => item.shouldHighlight) @@ -112,6 +124,10 @@ const Timetable: React.FC = ({ item, tintColor, stopCode }) => { setTableRows(stopRows); }, [estimate]) + if(error) { + return Something went wrong. Please try again later + } + return ( @@ -127,37 +143,37 @@ const Timetable: React.FC = ({ item, tintColor, stopCode }) => { marginRight: 16, }}> - { tableRows.map((row, rowIndex) => { - - + {tableRows.map((row, rowIndex) => { + + return ( - - { row.items.map((item, colIndex) => { + {row.items.map((item, colIndex) => { return ( - + key={colIndex}> {item.time} {item.live && - + } ) @@ -165,7 +181,7 @@ const Timetable: React.FC = ({ item, tintColor, stopCode }) => { ) })} - {item.stopTimes.length == 0 && !item.isEndOfRoute && Timetable Unavailable} + {item.stopTimes.length == 0 && !item.isEndOfRoute && Timetable Unavailable} ); diff --git a/app/index.tsx b/app/index.tsx index ff75d91..411a0ed 100644 --- a/app/index.tsx +++ b/app/index.tsx @@ -3,7 +3,7 @@ import { View, Alert } from 'react-native'; import { getAuthentication, getBaseData, getPatternPaths } from "aggie-spirit-api"; import { BottomSheetModal, BottomSheetModalProvider } from '@gorhom/bottom-sheet'; import useAppStore from './stores/useAppStore'; -import { GetBaseDataResponseSchema, GetPatternPathsResponseSchema, IGetBaseDataResponse, IGetPatternPathsResponse, IMapRoute } from "../utils/interfaces"; +import { GetBaseDataResponseSchema, GetPatternPathsResponseSchema, IGetPatternPathsResponse, IMapRoute } from "../utils/interfaces"; import MapView from './components/MapView'; import RoutesList from './components/sheets/RoutesList'; import AlertList from './components/sheets/AlertList'; @@ -33,24 +33,36 @@ const Home = () => { // Get the base data which includes routes (without patternPaths) and serviceInterruptions async function fetchBaseData(authToken: string) { + let baseData; + try { - return await getBaseData(authToken); + baseData = await getBaseData(authToken); } catch (error) { console.error(error); - throw new Error("Error while fetching base data"); + Alert.alert("Something went wrong", "Some features may not work correctly. Please try again later."); + + return; } + + return baseData; } // Fetch the pattern paths which are the route lines on the map async function fetchPatternPaths(routeKeys: string[], authToken: string) { + let patternPaths; + try { - return await getPatternPaths(routeKeys, authToken); + patternPaths = await getPatternPaths(routeKeys, authToken); } catch (error) { console.error(error); - throw new Error("Error while fetching pattern paths"); + Alert.alert("Something went wrong", "Some features may not work correctly. Please try again later."); + + return; } + + return patternPaths; } // Add each pattern path to the corresponding route @@ -70,9 +82,18 @@ const Home = () => { return; } - const baseData: IGetBaseDataResponse = await fetchBaseData(authToken); + const baseData = await fetchBaseData(authToken); + + if(!baseData) { + return; + } + const patternPathsResponse = await fetchPatternPaths(baseData.routes.map(route => route.key), authToken); + if(!patternPathsResponse) { + return; + } + // Add patternPaths to routes const routes = addPatternPathsToRoutes([...baseData.routes], patternPathsResponse); @@ -88,6 +109,8 @@ const Home = () => { console.error(error); Alert.alert("Something went wrong", "Some features may not work correctly. Please try again later."); + + return; } } From 6c6e1f10c87a4452670d7ca4e19c87a367cfe362 Mon Sep 17 00:00:00 2001 From: Sumit Nalavade Date: Wed, 17 Jan 2024 03:30:37 -0600 Subject: [PATCH 2/2] Fix lint errors --- app/components/ui/FavoritePill.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/ui/FavoritePill.tsx b/app/components/ui/FavoritePill.tsx index 167658b..c71c34c 100644 --- a/app/components/ui/FavoritePill.tsx +++ b/app/components/ui/FavoritePill.tsx @@ -1,5 +1,5 @@ import React, { memo, useEffect, useState } from 'react' -import { Text, TouchableOpacity, Alert } from 'react-native'; +import { TouchableOpacity, Alert } from 'react-native'; import AsyncStorage from '@react-native-async-storage/async-storage'; import {FontAwesome} from '@expo/vector-icons';