🚚 Rename NextPassagesDisplay/NextPassagesPanel (remove Next prefix)
This commit is contained in:
248
frontend/src/passagesDisplay.tsx
Normal file
248
frontend/src/passagesDisplay.tsx
Normal file
@@ -0,0 +1,248 @@
|
||||
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 { getTransportModeSrc } from "./types";
|
||||
import { BusinessDataContext } from "./businessData";
|
||||
import { PassagesPanel } from "./passagesPanel";
|
||||
|
||||
import { SearchContext } from "./search";
|
||||
|
||||
import styles from "./passagesDisplay.module.css";
|
||||
|
||||
|
||||
export const PassagesDisplay: Component = () => {
|
||||
const maxPassagePerPanel = 5;
|
||||
const syncPeriodMsec = 20 * 1000;
|
||||
|
||||
const { passages, getLinePassages, addPassages, clearPassages, serverUrl } = useContext(BusinessDataContext);
|
||||
const { getDisplayedStop } = useContext(SearchContext);
|
||||
|
||||
const [panels, setPanels] = createStore([]);
|
||||
const [displayedPanelId, setDisplayedPanelId] = createSignal<number>(0);
|
||||
|
||||
let _lines = new Map();
|
||||
|
||||
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=", getDisplayedStop());
|
||||
clearPassages();
|
||||
});
|
||||
|
||||
createEffect(async () => {
|
||||
console.log(`## OnPassageUpdate ${passages()} ##`);
|
||||
/* console.log(passages()); */
|
||||
await requestPassages();
|
||||
});
|
||||
|
||||
async function _fetchLine(lineId: string) {
|
||||
if (!_lines.has(lineId)) {
|
||||
const data = await fetch(`${serverUrl()}/line/${lineId}`, {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
const line = await data.json();
|
||||
_lines.set(line.id, line);
|
||||
}
|
||||
}
|
||||
|
||||
async function requestPassages() {
|
||||
console.log("### requestPassages ###");
|
||||
/* TODO: Manage several displays (one by stop) */
|
||||
const stops = getDisplayedStop();
|
||||
if (stops.length == 0) {
|
||||
return;
|
||||
}
|
||||
const stop = stops[0];
|
||||
|
||||
const httpOptions = { headers: { "Content-Type": "application/json" } };
|
||||
if (stop !== undefined) {
|
||||
const stopId = stop.id;
|
||||
console.log(`Fetching data for ${stopId}`);
|
||||
const url = `${serverUrl()}/stop/nextPassages/${stopId}`;
|
||||
/* console.log(`url=${url}`); */
|
||||
const data = await fetch(url, httpOptions);
|
||||
const response = await data.json();
|
||||
/* console.log(response); */
|
||||
const byLineByDstPassages = response.passages;
|
||||
/* console.log(byLineByDstPassages); */
|
||||
const linePromises = [];
|
||||
for (const lineId of Object.keys(byLineByDstPassages)) {
|
||||
linePromises.push(_fetchLine(lineId));
|
||||
}
|
||||
await Promise.all(linePromises);
|
||||
console.log("byLineByDstPassages=", byLineByDstPassages);
|
||||
// console.log("before addPassages passages=", passages());
|
||||
addPassages(byLineByDstPassages);
|
||||
console.log("AFTER passages=", passages());
|
||||
}
|
||||
}
|
||||
|
||||
setInterval(
|
||||
async () => {
|
||||
await requestPassages();
|
||||
},
|
||||
syncPeriodMsec
|
||||
);
|
||||
|
||||
// TODO: Sort transport modes by weight
|
||||
// TODO: Split this method to isolate the passagesPanel part.
|
||||
function _computeHeader(title: string): JSX.Element {
|
||||
let transportModes = [];
|
||||
transportModes = new Set(
|
||||
Object.keys(passages()).map((lineId) => {
|
||||
const line = _lines.get(lineId);
|
||||
if (line !== undefined) {
|
||||
return getTransportModeSrc(line.transportMode, false);
|
||||
}
|
||||
return null;
|
||||
})
|
||||
);
|
||||
return (
|
||||
<div class={styles.header}>
|
||||
<For each={Array.from(transportModes)}>
|
||||
{(transportMode) => {
|
||||
return (
|
||||
<div class={styles.transportMode}>
|
||||
<img src={transportMode} />
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</For>
|
||||
<div class={styles.title}>
|
||||
<svg viewbox="0 0 1260 50">
|
||||
<text
|
||||
x="0"
|
||||
y="50%"
|
||||
dominant-baseline="middle"
|
||||
font-size="50"
|
||||
style="fill: #ffffff"
|
||||
>
|
||||
{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>
|
||||
);
|
||||
}
|
||||
|
||||
function _computeFooter(): JSX.Element {
|
||||
return (
|
||||
<div class={styles.footer}>
|
||||
<For each={panels}>
|
||||
{(positioned) => {
|
||||
const { position, panel } = positioned;
|
||||
const circleStyle = {
|
||||
fill: `var(--idfm-${position == displayedPanelId() ? "white" : "black"
|
||||
})`,
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<svg viewBox="0 0 29 29">
|
||||
<circle
|
||||
cx="50%"
|
||||
cy="50%"
|
||||
r="13"
|
||||
stroke="#ffffff"
|
||||
stroke-width="3"
|
||||
style={circleStyle}
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</For>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const mainDivClasses = `${styles.PassagesDisplay} ${styles.ar16x9}`;
|
||||
return (
|
||||
<div class={mainDivClasses}>
|
||||
{_computeHeader("Prochains 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 {
|
||||
console.log("chunk=", chunk);
|
||||
const [store] = createStore(chunk);
|
||||
const panelid = index++;
|
||||
const panel = (
|
||||
<PassagesPanel
|
||||
show={panelid == displayedPanelId()}
|
||||
passages={store}
|
||||
lines={_lines} />
|
||||
);
|
||||
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}
|
||||
lines={_lines} />
|
||||
);
|
||||
newPanels.push(panel);
|
||||
positioneds.push({ position: panelId, panel });
|
||||
}
|
||||
|
||||
setPanels(positioneds);
|
||||
return newPanels;
|
||||
}}
|
||||
</div>
|
||||
{_computeFooter()}
|
||||
</div>
|
||||
);
|
||||
};
|
Reference in New Issue
Block a user