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