180 lines
5.0 KiB
TypeScript
180 lines
5.0 KiB
TypeScript
import { Component, createEffect, createSignal, useContext } from "solid-js";
|
|
import { createStore } from "solid-js/store";
|
|
import { createDateNow } from "@solid-primitives/date";
|
|
import { format } from "date-fns";
|
|
|
|
import { BusinessDataContext } from "./businessData";
|
|
import { SearchContext } from "./search";
|
|
|
|
import { PassagesPanel } from "./passagesPanel";
|
|
import { getTransportModeSrc } from "./utils";
|
|
|
|
import styles from "./passagesDisplay.module.css";
|
|
|
|
|
|
export const PassagesDisplay: Component = () => {
|
|
const maxPassagePerPanel = 5;
|
|
const syncPeriodMsec = 20 * 1000;
|
|
|
|
const { passages, getLine, getLinePassages, refreshPassages, clearPassages } = useContext(BusinessDataContext);
|
|
|
|
// TODO: Use props instead
|
|
const { getDisplayedStops } = useContext(SearchContext);
|
|
|
|
const [displayedPanelId, setDisplayedPanelId] = createSignal<number>(0);
|
|
const [panels, setPanels] = createStore([]);
|
|
|
|
const [dateNow] = createDateNow(1000);
|
|
|
|
setInterval(() => {
|
|
let nextPanelId = displayedPanelId() + 1;
|
|
if (nextPanelId >= panels.length) {
|
|
nextPanelId = 0;
|
|
}
|
|
setDisplayedPanelId(nextPanelId);
|
|
}, 4000);
|
|
|
|
createEffect(() => {
|
|
console.log("######### onStopIdUpdate #########");
|
|
// Track local.stopIp to force dependency.
|
|
console.log("getDisplayedStop=", getDisplayedStops());
|
|
clearPassages();
|
|
});
|
|
|
|
createEffect(async () => {
|
|
console.log(`## OnPassageUpdate ${passages()} ##`);
|
|
const stops = getDisplayedStops();
|
|
if (stops.length > 0) {
|
|
refreshPassages(stops[0].id);
|
|
}
|
|
});
|
|
|
|
setInterval(
|
|
async () => {
|
|
const stops = getDisplayedStops();
|
|
if (stops.length > 0) {
|
|
refreshPassages(stops[0].id);
|
|
}
|
|
},
|
|
syncPeriodMsec
|
|
);
|
|
|
|
// TODO: Sort transport modes by weight
|
|
const Header: Component = (props) => {
|
|
const computeTransportModes = async (lineIds: Array<number>) => {
|
|
const lines = await Promise.all(lineIds.map((lineId) => getLine(lineId)));
|
|
return new Set(lines.map((line) => getTransportModeSrc(line.transportMode, false)));
|
|
}
|
|
|
|
const [linesIds, setLinesIds] = createSignal([]);
|
|
const [transportModeUrls] = createResource(linesIds, computeTransportModes);
|
|
|
|
createEffect(() => {
|
|
setLinesIds(Object.keys(props.passages));
|
|
});
|
|
|
|
return (
|
|
<div class={styles.header}>
|
|
<Show when={transportModeUrls() !== undefined} >
|
|
<For each={Array.from(transportModeUrls())}>
|
|
{(url) =>
|
|
<div class={styles.transportMode}>
|
|
<img src={url} />
|
|
</div>
|
|
}
|
|
</For>
|
|
</Show>
|
|
<div class={styles.title}>
|
|
<svg viewbox="0 0 1260 50">
|
|
<text x="0" y="50%" dominant-baseline="middle" font-size="50" style="fill: #ffffff">
|
|
{props.title}
|
|
</text>
|
|
</svg>
|
|
</div>
|
|
<div class={styles.clock}>
|
|
<svg viewbox="0 0 115 43">
|
|
<text x="50%" y="55%" dominant-baseline="middle" text-anchor="middle" font-size="43" style="fill: #ffffff">
|
|
{format(dateNow(), "HH:mm")}
|
|
</text>
|
|
</svg>
|
|
</div>
|
|
</div >
|
|
);
|
|
};
|
|
|
|
const Footer: Component = (props) => {
|
|
return (
|
|
<div class={styles.footer}>
|
|
<For each={props.panels}>
|
|
{(positioned) => {
|
|
const { position } = positioned;
|
|
return (
|
|
<div>
|
|
<svg viewBox="0 0 29 29">
|
|
<circle cx="50%" cy="50%" r="13" stroke="#ffffff" stroke-width="3"
|
|
style={{ fill: `var(--idfm-${position == displayedPanelId() ? "white" : "black"})` }}
|
|
/>
|
|
</svg>
|
|
</div>
|
|
);
|
|
}}
|
|
</For>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div class={styles.passagesDisplay}>
|
|
<Header title="Prochains passages" passages={passages()} />
|
|
<div class={styles.panelsContainer}>
|
|
{() => {
|
|
setPanels([]);
|
|
|
|
let newPanels = [];
|
|
let positioneds = [];
|
|
let index = 0;
|
|
|
|
let chunk = {};
|
|
let chunkSize = 0;
|
|
|
|
console.log("passages=", passages());
|
|
for (const lineId of Object.keys(passages())) {
|
|
console.log("lineId=", lineId);
|
|
const byLinePassages = getLinePassages(lineId);
|
|
console.log("byLinePassages=", byLinePassages);
|
|
const byLinePassagesKeys = Object.keys(byLinePassages);
|
|
console.log("byLinePassagesKeys=", byLinePassagesKeys);
|
|
|
|
if (byLinePassagesKeys.length <= maxPassagePerPanel - chunkSize) {
|
|
chunk[lineId] = byLinePassages;
|
|
chunkSize += byLinePassagesKeys.length;
|
|
}
|
|
else {
|
|
const [store] = createStore(chunk);
|
|
const panelid = index++;
|
|
const panel = <PassagesPanel show={panelid == displayedPanelId()} passages={store} />;
|
|
newPanels.push(panel);
|
|
positioneds.push({ position: panelid, panel });
|
|
|
|
chunk = {};
|
|
chunk[lineId] = byLinePassages;
|
|
chunkSize = byLinePassagesKeys.length;
|
|
}
|
|
}
|
|
if (chunkSize) {
|
|
const panelId = index++;
|
|
const [store] = createStore(chunk);
|
|
const panel = <PassagesPanel show={panelId == displayedPanelId()} passages={store} />;
|
|
newPanels.push(panel);
|
|
positioneds.push({ position: panelId, panel });
|
|
}
|
|
|
|
setPanels(positioneds);
|
|
return newPanels;
|
|
}}
|
|
</div>
|
|
<Footer panels={panels} />
|
|
</div>
|
|
);
|
|
};
|