Use local cache to optimize display
This commit is contained in:
parent
edbc01122d
commit
311a29cf4b
68
sncf-station/package-lock.json
generated
68
sncf-station/package-lock.json
generated
@ -12,6 +12,9 @@
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@mui/material": "^5.15.6",
|
||||
"@mui/x-date-pickers": "^6.19.2",
|
||||
"@tanstack/query-sync-storage-persister": "^5.18.0",
|
||||
"@tanstack/react-query": "^5.18.0",
|
||||
"@tanstack/react-query-persist-client": "^5.18.0",
|
||||
"@testing-library/jest-dom": "^5.17.0",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
@ -4181,6 +4184,71 @@
|
||||
"url": "https://github.com/sponsors/gregberge"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/query-core": {
|
||||
"version": "5.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.18.0.tgz",
|
||||
"integrity": "sha512-8c6nxeAnGHxIDZIyDmHdmgFt4D+LprAQaJmjsnM4szcIjsWOyFlzxdqQUuQ/XuQRvUgqYaqlJTtDADlSS7pTPQ==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/query-persist-client-core": {
|
||||
"version": "5.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/query-persist-client-core/-/query-persist-client-core-5.18.0.tgz",
|
||||
"integrity": "sha512-CXmUP8GYW1LL9tQC8vmuaK3XKWR3GaP8Bhy2bpgbIX5aOTN0qWL4zVema4hclgNahpTlY13P67Biab8ieB6frA==",
|
||||
"dependencies": {
|
||||
"@tanstack/query-core": "5.18.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/query-sync-storage-persister": {
|
||||
"version": "5.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/query-sync-storage-persister/-/query-sync-storage-persister-5.18.0.tgz",
|
||||
"integrity": "sha512-a+ztQhpnWRmQN8kn/IVscgxmSnBZER2TbrLkocZGzowecxT+Lm0RzbX+Dl2lVz92XXHFmhAjAXhWG61rbqQTng==",
|
||||
"dependencies": {
|
||||
"@tanstack/query-core": "5.18.0",
|
||||
"@tanstack/query-persist-client-core": "5.18.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/react-query": {
|
||||
"version": "5.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.18.0.tgz",
|
||||
"integrity": "sha512-7FKxNfxxKEL7n3ADpwp81Fy4FX85hNkYVzQQVQsF0JAPl93c3d1gmNZMIbEtOqgYfom1/ontGh3FiZGYj3xyWA==",
|
||||
"dependencies": {
|
||||
"@tanstack/query-core": "5.18.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/react-query-persist-client": {
|
||||
"version": "5.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-query-persist-client/-/react-query-persist-client-5.18.0.tgz",
|
||||
"integrity": "sha512-XS3C3tMcnBosptRhn4kN5RBs6j8iRiE7tVr7XN73We4o3VNo86+zEe4iOwO+ziTq0Dawnc4kCjUVS3tZ2DiKhg==",
|
||||
"dependencies": {
|
||||
"@tanstack/query-persist-client-core": "5.18.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tanstack/react-query": "^5.18.0",
|
||||
"react": "^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@testing-library/dom": {
|
||||
"version": "9.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz",
|
||||
|
@ -7,6 +7,9 @@
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@mui/material": "^5.15.6",
|
||||
"@mui/x-date-pickers": "^6.19.2",
|
||||
"@tanstack/query-sync-storage-persister": "^5.18.0",
|
||||
"@tanstack/react-query": "^5.18.0",
|
||||
"@tanstack/react-query-persist-client": "^5.18.0",
|
||||
"@testing-library/jest-dom": "^5.17.0",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
|
@ -6,6 +6,9 @@ import {frFR, LocalizationProvider} from "@mui/x-date-pickers"
|
||||
import {AdapterDayjs} from "@mui/x-date-pickers/AdapterDayjs"
|
||||
import 'dayjs/locale/fr'
|
||||
import './App.css'
|
||||
import {QueryClient, QueryClientProvider} from "@tanstack/react-query";
|
||||
import {createSyncStoragePersister} from "@tanstack/query-sync-storage-persister";
|
||||
import {persistQueryClient} from "@tanstack/react-query-persist-client";
|
||||
|
||||
function App() {
|
||||
const router = createBrowserRouter([
|
||||
@ -40,11 +43,32 @@ function App() {
|
||||
[prefersDarkMode],
|
||||
);
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
gcTime: 1000 * 60 * 60 * 24, // 3 hours
|
||||
staleTime: 1000 * 60 * 60 * 3, // 3 hours
|
||||
notifyOnChangeProps: ['data', 'error'],
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const localStoragePersister = createSyncStoragePersister({
|
||||
storage: window.localStorage,
|
||||
})
|
||||
|
||||
persistQueryClient({
|
||||
queryClient,
|
||||
persister: localStoragePersister,
|
||||
})
|
||||
|
||||
return <>
|
||||
<ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
<LocalizationProvider dateAdapter={AdapterDayjs} localeText={frFR.components.MuiLocalizationProvider.defaultProps.localeText} adapterLocale="fr">
|
||||
<RouterProvider router={router} />
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<RouterProvider router={router} />
|
||||
</QueryClientProvider>
|
||||
</LocalizationProvider>
|
||||
</ThemeProvider>
|
||||
</>
|
||||
|
@ -1,9 +1,10 @@
|
||||
import {useParams, useSearchParams} from "react-router-dom"
|
||||
import TrainsTable from "./TrainsTable"
|
||||
import {useEffect, useState} from "react";
|
||||
import {Box, Button, FormControl, FormControlLabel, FormGroup, FormLabel} from "@mui/material";
|
||||
import {DatePicker, DateTimePicker, TimePicker} from "@mui/x-date-pickers";
|
||||
import {useState} from "react";
|
||||
import {Box, Button, FormLabel} from "@mui/material";
|
||||
import {DatePicker, TimePicker} from "@mui/x-date-pickers";
|
||||
import dayjs from "dayjs";
|
||||
import {useQuery, useQueryClient} from "@tanstack/react-query";
|
||||
|
||||
function DateTimeSelector({date, time}) {
|
||||
return <>
|
||||
@ -20,7 +21,6 @@ function DateTimeSelector({date, time}) {
|
||||
|
||||
function Station() {
|
||||
let {stopId} = useParams()
|
||||
let [stop, setStop] = useState({'name': "Chargement…"})
|
||||
let [searchParams, _setSearchParams] = useSearchParams()
|
||||
const now = new Date()
|
||||
let dateNow = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`
|
||||
@ -28,13 +28,14 @@ function Station() {
|
||||
let [date, setDate] = useState(searchParams.get('date') || dateNow)
|
||||
let [time, setTime] = useState(searchParams.get('time') || timeNow)
|
||||
|
||||
useEffect(() => {
|
||||
fetch(`http://localhost:8000/api/gtfs/stop/${stopId}/`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
setStop(data)
|
||||
})
|
||||
}, [stopId])
|
||||
useQueryClient()
|
||||
const stopQuery = useQuery({
|
||||
queryKey: ['stop', stopId],
|
||||
queryFn: () => fetch(`http://localhost:8000/api/gtfs/stop/${stopId}/`)
|
||||
.then(response => response.json()),
|
||||
enabled: !!stopId,
|
||||
})
|
||||
const stop = stopQuery.data ?? {name: "Chargement…"}
|
||||
|
||||
if (time === timeNow) {
|
||||
setInterval(() => {
|
||||
@ -43,7 +44,7 @@ function Station() {
|
||||
let timeNow = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`
|
||||
setDate(dateNow)
|
||||
setTime(timeNow)
|
||||
}, 60000)
|
||||
}, 5000)
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -1,4 +1,3 @@
|
||||
import {useEffect, useState} from "react"
|
||||
import {
|
||||
Box,
|
||||
styled,
|
||||
@ -11,6 +10,8 @@ import {
|
||||
Typography
|
||||
} from "@mui/material"
|
||||
import {CSSTransition, TransitionGroup} from 'react-transition-group'
|
||||
import {useQueries, useQuery, useQueryClient} from "@tanstack/react-query";
|
||||
import {useEffect, useState} from "react";
|
||||
|
||||
const StyledTableRow = styled(TableRow)(({ theme, tableType }) => ({
|
||||
'tbody &:nth-of-type(odd)': {
|
||||
@ -49,27 +50,37 @@ function TrainsTableHeader({tableType}) {
|
||||
}
|
||||
|
||||
function TrainsTableBody({stop, date, time, tableType}) {
|
||||
const [trains, setTrains] = useState([])
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
useEffect(() => {
|
||||
if (stop.id !== undefined) {
|
||||
let validTrains = trains.filter(train => {
|
||||
if (tableType === "departures")
|
||||
return `${train.departure_date}T${train.departure_time_24h}` >= `${date}T${time}`
|
||||
else
|
||||
return `${train.arrival_date}T${train.arrival_time_24h}` >= `${date}T${time}`
|
||||
})
|
||||
if (trains.length > 0 && validTrains.length === trains.length)
|
||||
return
|
||||
console.log(`${trains.length - validTrains.length} trains deleted`)
|
||||
fetch(`http://localhost:8000/api/station/next_${tableType}/?stop_id=${stop.id}&date=${date}&time=${time}&offset=${validTrains.length}&limit=${20 - validTrains.length}`)
|
||||
let filterTime = (train) => {
|
||||
if (train.departure_time === "04:56:00")
|
||||
return false
|
||||
if (tableType === "departures")
|
||||
return `${train.departure_date}T${train.departure_time_24h}` >= `${date}T${time}`
|
||||
else
|
||||
return `${train.arrival_date}T${train.arrival_time_24h}` >= `${date}T${time}`
|
||||
}
|
||||
|
||||
function updateTrains() {
|
||||
return fetch(`http://localhost:8000/api/station/next_${tableType}/?stop_id=${stop.id}&date=${date}&time=${time}&offset=${0}&limit=${20}`)
|
||||
.then(response => response.json())
|
||||
.then(data => data.results)
|
||||
.then(data => {
|
||||
setTrains(trains => [...validTrains, ...data])
|
||||
})
|
||||
}
|
||||
}, [stop, tableType, date, time])
|
||||
.then(data => [...data])
|
||||
}
|
||||
|
||||
const trainsQuery = useQuery({
|
||||
queryKey: ['trains', stop.id, tableType],
|
||||
queryFn: updateTrains,
|
||||
enabled: !!stop.id,
|
||||
})
|
||||
const trains = trainsQuery.data ?? []
|
||||
|
||||
useEffect(() => {
|
||||
let validTrains = trains?.filter(filterTime) ?? []
|
||||
console.log(validTrains.length)
|
||||
if (trains?.length > 0 && validTrains.length <= trains?.length)
|
||||
queryClient.invalidateQueries({queryKey: ['trains', stop.id, tableType]})
|
||||
}, [stop.id, tableType, date, time])
|
||||
|
||||
let table_rows = trains.map((train) => <CSSTransition key={train.id} timeout={500} classNames="shrink">
|
||||
<TrainRow train={train} tableType={tableType} />
|
||||
@ -85,64 +96,51 @@ function TrainsTableBody({stop, date, time, tableType}) {
|
||||
}
|
||||
|
||||
function TrainRow({train, tableType}) {
|
||||
const [trip, setTrip] = useState({})
|
||||
const [route, setRoute] = useState({})
|
||||
const [stopTimes, setStopTimes] = useState([])
|
||||
const [trainType, setTrainType] = useState("")
|
||||
const tripQuery = useQuery({
|
||||
queryKey: ['trip', train.trip],
|
||||
queryFn: () => fetch(`http://localhost:8000/api/gtfs/trip/${train.trip}/`)
|
||||
.then(response => response.json()),
|
||||
enabled: !!train.trip,
|
||||
})
|
||||
const trip = tripQuery.data ?? {}
|
||||
|
||||
useEffect(() => {
|
||||
if (train.trip !== undefined) {
|
||||
fetch(`http://localhost:8000/api/gtfs/trip/${train.trip}/`)
|
||||
const routeQuery = useQuery({
|
||||
queryKey: ['route', trip.route],
|
||||
queryFn: () => fetch(`http://localhost:8000/api/gtfs/route/${trip.route}/`)
|
||||
.then(response => response.json()),
|
||||
enabled: !!trip.route,
|
||||
})
|
||||
const route = routeQuery.data ?? {}
|
||||
const trainType = getTrainType(train, route)
|
||||
|
||||
const stopTimesQuery = useQuery({
|
||||
queryKey: ['stop_times', trip.id],
|
||||
queryFn: () => fetch(`http://localhost:8000/api/gtfs/stop_time/?trip=${trip.id}&order=stop_sequence&limit=1000`)
|
||||
.then(response => response.json())
|
||||
.then(t => {
|
||||
t.stop_times = []
|
||||
setTrip(t)
|
||||
})
|
||||
}
|
||||
}, [train.trip])
|
||||
.then(data => data.results),
|
||||
enabled: !!trip.id,
|
||||
})
|
||||
const stopTimes = stopTimesQuery.data ?? []
|
||||
const stopIds = stopTimes.map(stop_time => stop_time.stop)
|
||||
|
||||
useEffect(() => {
|
||||
if (trip.route !== undefined) {
|
||||
fetch(`http://localhost:8000/api/gtfs/route/${trip.route}/`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
setRoute(data)
|
||||
})
|
||||
}
|
||||
}, [trip.route])
|
||||
const stopQueries = useQueries({
|
||||
queries: stopIds.map(stopId => ({
|
||||
queryKey: ['stop', stopId],
|
||||
queryFn: () => fetch(`http://localhost:8000/api/gtfs/stop/${stopId}/`)
|
||||
.then(response => response.json()),
|
||||
enabled: !!stopId,
|
||||
})),
|
||||
})
|
||||
const stops = stopTimes.map(((stopTime, i) => ({...stopTime, stop: stopQueries[i]?.data ?? {"name": "…"}}))) ?? []
|
||||
|
||||
useEffect(() => {
|
||||
if (route !== undefined) {
|
||||
setTrainType(getTrainType(train, route))
|
||||
}
|
||||
}, [train, route]);
|
||||
let headline = stops[tableType === "departures" ? stops.length - 1 : 0]?.stop ?? {name: "Chargement…"}
|
||||
|
||||
useEffect(() => {
|
||||
if (trip.route !== undefined) {
|
||||
fetch(`http://localhost:8000/api/gtfs/stop_time/?trip=${trip.id}&order=stop_sequence&limit=1000`)
|
||||
.then(response => response.json())
|
||||
.then(data => data.results)
|
||||
.then(stop_times => {
|
||||
Promise.all(stop_times.map(stop_time =>
|
||||
fetch(`http://localhost:8000/api/gtfs/stop/${stop_time.stop}/`).then(response => response.json())
|
||||
)).then(stops => {
|
||||
setStopTimes(stop_times.map((stop_time, index) => {
|
||||
stop_time.stop = stops[index]
|
||||
return stop_time
|
||||
}))
|
||||
})
|
||||
})
|
||||
}
|
||||
}, [trip.route])
|
||||
|
||||
let headline = stopTimes[tableType === "departures" ? stopTimes.length - 1 : 0]?.stop ?? {name: "Chargement…"}
|
||||
|
||||
let stops_filter
|
||||
let stopsFilter
|
||||
if (tableType === "departures")
|
||||
stops_filter = (stop_time) => stop_time.stop_sequence > train.stop_sequence && stop_time.drop_off_type === 0
|
||||
stopsFilter = (stop_time) => stop_time.stop_sequence > train.stop_sequence && stop_time.drop_off_type === 0
|
||||
else
|
||||
stops_filter = (stop_time) => stop_time.stop_sequence < train.stop_sequence && stop_time.pickup_type === 0
|
||||
let stops_names = stopTimes.filter(stops_filter).map(stop_time => stop_time?.stop.name ?? "").join(", ")
|
||||
stopsFilter = (stop_time) => stop_time.stop_sequence < train.stop_sequence && stop_time.pickup_type === 0
|
||||
let stopsNames = stops.filter(stopsFilter).map(stopTime => stopTime?.stop.name ?? "").join(", ") ?? ""
|
||||
|
||||
return <>
|
||||
<StyledTableRow tableType={tableType}>
|
||||
@ -176,8 +174,8 @@ function TrainRow({train, tableType}) {
|
||||
</Box>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Typography fontSize={24} fontWeight="bold">{headline.name}</Typography>
|
||||
<span className="stops">{stops_names}</span>
|
||||
<Typography fontSize={24} fontWeight="bold" data-stop-id={headline.id}>{headline.name}</Typography>
|
||||
<span className="stops">{stopsNames}</span>
|
||||
</TableCell>
|
||||
</StyledTableRow>
|
||||
</>
|
||||
|
Loading…
Reference in New Issue
Block a user