import { batch, createContext, createSignal, JSX } from 'solid-js'; import { createStore } from 'solid-js/store'; import { Line, Lines, Passage, Passages, Stop, Stops } from './types'; export interface BusinessDataStore { getLine: (lineId: string) => Promise; getLinePassages: (lineId: string) => Record; getLineDestinations: (lineId: string) => string[]; getDestinationPassages: (lineId: string, destination: string) => Passage[]; passages: () => Passages; getPassagesLineIds: () => string[]; refreshPassages: (stopId: number) => Promise; addPassages: (passages: Passages) => void; clearPassages: () => void; getStop: (stopId: number) => Stop | undefined; searchStopByName: (name: string) => Promise; }; export const BusinessDataContext = createContext(); export function BusinessDataProvider(props: { children: JSX.Element }) { const [serverUrl] = createSignal("https://localhost:4443"); type Store = { lines: Lines; passages: Passages; stops: Stops; }; const [store, setStore] = createStore({ lines: {}, passages: {}, stops: {} }); const getLine = async (lineId: string): Promise => { let line = store.lines[lineId]; if (line === undefined) { console.log(`${lineId} not found... fetch it from backend.`); const data = await fetch(`${serverUrl()}/line/${lineId}`, { headers: { 'Content-Type': 'application/json' } }); line = await data.json(); setStore('lines', lineId, line); } return line; } const getLinePassages = (lineId: string): Record => { return store.passages[lineId]; } const getLineDestinations = (lineId: string): string[] => { return Object.keys(store.passages[lineId]); } const getDestinationPassages = (lineId: string, destination: string): Passage[] => { return store.passages[lineId][destination]; } const passages = (): Passages => { return store.passages; } const getPassagesLineIds = (): string[] => { return Object.keys(store.passages); } const _cleanupPassages = (passages: Passages): void => { const deadline = Math.floor(Date.now() / 1000) - 60; for (const linePassages of Object.values(passages)) { for (const destination of Object.keys(linePassages)) { const destinationPassages = linePassages[destination]; const cleaned = []; for (const passage of destinationPassages) { if (passage.expectedDepartTs > deadline) { cleaned.push(passage); } } linePassages[destination] = cleaned; } } } const refreshPassages = async (stopId: number): Promise => { const httpOptions = { headers: { "Content-Type": "application/json" } }; console.log(`Fetching data for ${stopId}`); const data = await fetch(`${serverUrl()}/stop/nextPassages/${stopId}`, httpOptions); const response = await data.json(); _cleanupPassages(response.passages); addPassages(response.passages); } const addPassages = (passages: Passages): void => { batch(() => { const storePassages = store.passages; for (const lineId of Object.keys(passages)) { const newLinePassages = passages[lineId]; const linePassages = storePassages[lineId]; if (linePassages === undefined) { setStore('passages', lineId, newLinePassages); } else { for (const destination of Object.keys(newLinePassages)) { const newLinePassagesDestination = newLinePassages[destination]; const linePassagesDestination = linePassages[destination]; if (linePassagesDestination === undefined) { setStore('passages', lineId, destination, newLinePassagesDestination); } else { if (linePassagesDestination.length - newLinePassagesDestination.length != 0) { console.log(`Server provides ${newLinePassagesDestination.length} passages, \ ${linePassagesDestination.length} here... refresh all them.`); setStore('passages', lineId, destination, newLinePassagesDestination); } else { linePassagesDestination.forEach((passage, index) => { const newPassage = newLinePassagesDestination[index]; if (passage.expectedDepartTs != newPassage.expectedDepartTs) { console.log(`Refresh expectedDepartTs (${passage.expectedDepartTs} -> ${newPassage.expectedDepartTs}`); setStore('passages', lineId, destination, index, 'expectedDepartTs', newPassage.expectedDepartTs); } if (passage.expectedArrivalTs != newPassage.expectedArrivalTs) { console.log(`Refresh expectedArrivalTs (${passage.expectedArrivalTs} -> ${newPassage.expectedArrivalTs}`); setStore('passages', lineId, destination, index, 'expectedArrivalTs', newPassage.expectedArrivalTs); } }); } } } } } }); } const clearPassages = (): void => { setStore((s: Store): Store => { for (const lineId of Object.keys(s.passages)) { setStore('passages', lineId, undefined); } return s; }); } const getStop = (stopId: number): Stop | undefined => { return store.stops[stopId]; } const searchStopByName = async (name: string): Promise => { const data = await fetch(`${serverUrl()}/stop/?name=${name}`, { headers: { 'Content-Type': 'application/json' } }); const stops = await data.json(); const byIdStops: Stops = {}; for (const stop of stops) { byIdStops[stop.id] = stop; setStore('stops', stop.id, stop); } return byIdStops; } return ( {props.children} ); } export interface BusinessDataStore { getLine: (lineId: string) => Promise; getLinePassages: (lineId: string) => Record; passages: () => Passages; refreshPassages: (stopId: number) => Promise; addPassages: (passages: Passages) => void; clearPassages: () => void; getStop: (stopId: number) => Stop | undefined; searchStopByName: (name: string) => Promise; };