186 lines
6.0 KiB
TypeScript
186 lines
6.0 KiB
TypeScript
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<Line>;
|
|
getLinePassages: (lineId: string) => Record<string, Passage[]>;
|
|
getLineDestinations: (lineId: string) => string[];
|
|
getDestinationPassages: (lineId: string, destination: string) => Passage[];
|
|
|
|
passages: () => Passages;
|
|
getPassagesLineIds: () => string[];
|
|
refreshPassages: (stopId: number) => Promise<void>;
|
|
addPassages: (passages: Passages) => void;
|
|
clearPassages: () => void;
|
|
|
|
getStop: (stopId: number) => Stop | undefined;
|
|
searchStopByName: (name: string) => Promise<Stops>;
|
|
};
|
|
|
|
export const BusinessDataContext = createContext<BusinessDataStore>();
|
|
|
|
export function BusinessDataProvider(props: { children: JSX.Element }) {
|
|
|
|
const [serverUrl] = createSignal<string>("https://localhost:4443");
|
|
|
|
type Store = {
|
|
lines: Lines;
|
|
passages: Passages;
|
|
stops: Stops;
|
|
};
|
|
|
|
const [store, setStore] = createStore<Store>({ lines: {}, passages: {}, stops: {} });
|
|
|
|
const getLine = async (lineId: string): Promise<Line> => {
|
|
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<string, Passage[]> => {
|
|
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<void> => {
|
|
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<Stops> => {
|
|
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 (
|
|
<BusinessDataContext.Provider value={{
|
|
getLine, getLinePassages, getLineDestinations, getDestinationPassages, passages, getPassagesLineIds,
|
|
refreshPassages, addPassages, clearPassages, getStop, searchStopByName
|
|
}}>
|
|
{props.children}
|
|
</BusinessDataContext.Provider>
|
|
);
|
|
}
|
|
|
|
export interface BusinessDataStore {
|
|
getLine: (lineId: string) => Promise<Line>;
|
|
getLinePassages: (lineId: string) => Record<string, Passage[]>;
|
|
|
|
passages: () => Passages;
|
|
refreshPassages: (stopId: number) => Promise<void>;
|
|
addPassages: (passages: Passages) => void;
|
|
clearPassages: () => void;
|
|
|
|
getStop: (stopId: number) => Stop | undefined;
|
|
searchStopByName: (name: string) => Promise<Stops>;
|
|
};
|