From 5c08780f98d46c24dcb34af37f345944504e5322 Mon Sep 17 00:00:00 2001 From: Adrien Date: Sun, 12 Feb 2023 19:01:32 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20Reduce=20the=20refresh=20o?= =?UTF-8?q?n=20passages=20update=20to=20the=20TtwPassage=20component?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/businessData.tsx | 72 ++++++++++++++++----- frontend/src/passagesDisplay.tsx | 28 ++++----- frontend/src/passagesPanel.tsx | 103 ++++++++++++++++--------------- 3 files changed, 121 insertions(+), 82 deletions(-) diff --git a/frontend/src/businessData.tsx b/frontend/src/businessData.tsx index 07fb962..53355f4 100644 --- a/frontend/src/businessData.tsx +++ b/frontend/src/businessData.tsx @@ -1,4 +1,4 @@ -import { createContext, createSignal, JSX } from 'solid-js'; +import { batch, createContext, createSignal, JSX } from 'solid-js'; import { createStore } from 'solid-js/store'; import { Line, Lines, Passage, Passages, Stop, Stops } from './types'; @@ -7,8 +7,11 @@ 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; @@ -46,12 +49,24 @@ export function BusinessDataProvider(props: { children: JSX.Element }) { 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)) { @@ -64,7 +79,6 @@ export function BusinessDataProvider(props: { children: JSX.Element }) { } } linePassages[destination] = cleaned; - } } } @@ -79,20 +93,45 @@ export function BusinessDataProvider(props: { children: JSX.Element }) { } const addPassages = (passages: Passages): void => { - 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]; - setStore('passages', lineId, destination, newLinePassagesDestination); + 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 => { @@ -124,7 +163,8 @@ export function BusinessDataProvider(props: { children: JSX.Element }) { return ( {props.children} diff --git a/frontend/src/passagesDisplay.tsx b/frontend/src/passagesDisplay.tsx index 15de72c..7873ecd 100644 --- a/frontend/src/passagesDisplay.tsx +++ b/frontend/src/passagesDisplay.tsx @@ -202,7 +202,7 @@ const Body: ParentComponent<{ maxPassagesPerPanel: number, syncPeriodMsec: numbe return
; } - const { getLinePassages, passages, clearPassages, refreshPassages } = businessDataStore; + const { getLinePassages, getLineDestinations, passages, getPassagesLineIds, clearPassages, refreshPassages } = businessDataStore; const { isPassagesRefreshEnabled, getDisplayedPanelId, setDisplayedPanelId, getPanels, setPanels } = passagesDisplayStore; const { getDisplayedStops } = searchStore; @@ -253,31 +253,29 @@ const Body: ParentComponent<{ maxPassagesPerPanel: number, syncPeriodMsec: numbe let positioneds: PositionnedPanel[] = []; let index = 0; - let chunk: Record> = {}; - let chunkSize = 0; + let lineIds: string[] = []; + let destinationsNb = 0; - for (const lineId of Object.keys(passages())) { - const byLinePassages = getLinePassages(lineId); - const byLinePassagesKeys = Object.keys(byLinePassages); + for (const lineId of getPassagesLineIds()) { + const lineDestinations = getLineDestinations(lineId); - if (byLinePassagesKeys.length <= props.maxPassagesPerPanel - chunkSize) { - chunk[lineId] = byLinePassages; - chunkSize += byLinePassagesKeys.length; + if (lineDestinations.length <= props.maxPassagesPerPanel - destinationsNb) { + lineIds.push(lineId); + destinationsNb += lineDestinations.length; } else { const panelid = index++; - const panel = ; + const panel = ; newPanels.push(panel); positioneds.push({ position: panelid, panel: panel }); - chunk = {}; - chunk[lineId] = byLinePassages; - chunkSize = byLinePassagesKeys.length; + lineIds = [lineId]; + destinationsNb = lineDestinations.length; } } - if (chunkSize) { + if (destinationsNb) { const panelId = index++; - const panel = ; + const panel = ; newPanels.push(panel); positioneds.push({ position: panelId, panel: panel }); } diff --git a/frontend/src/passagesPanel.tsx b/frontend/src/passagesPanel.tsx index 20fc74f..359c0f5 100644 --- a/frontend/src/passagesPanel.tsx +++ b/frontend/src/passagesPanel.tsx @@ -1,41 +1,15 @@ -import { VoidComponent, createEffect, createResource, createSignal, ParentComponent, ParentProps, Show, useContext, For } from 'solid-js'; +import { VoidComponent, createResource, ParentComponent, ParentProps, Show, useContext, For } from 'solid-js'; import { createDateNow, getTime } from '@solid-primitives/date'; import { AnimationOptions } from '@motionone/types'; import { Motion } from "@motionone/solid"; -import { Line, Passage, Passages, TrafficStatus } from './types'; +import { Line, TrafficStatus } from './types'; import { renderLineTransportMode, renderLinePicto } from './utils'; import { BusinessDataContext, BusinessDataStore } from "./businessData"; import styles from './passagesPanel.module.css'; -const TtwPassage: VoidComponent<{ passage: Passage, style: string, fontSize: number }> = (props) => { - - const [dateNow] = createDateNow(5000); - - const refTs = props.passage.expectedDepartTs !== null ? props.passage.expectedDepartTs : props.passage.expectedArrivalTs; - const ttwSec = refTs - (getTime(dateNow()) / 1000); - const isApproaching = ttwSec <= 60; - - const transition: AnimationOptions = { duration: 3, repeat: Infinity }; - return ( -
- - - {Math.floor(ttwSec / 60)} min - - -
- ); -} - const UnavailablePassage: VoidComponent<{ style: string }> = (props) => { const textStyle = { fill: "#000000" }; @@ -50,8 +24,47 @@ const UnavailablePassage: VoidComponent<{ style: string }> = (props) => { ); } +const TtwPassage: VoidComponent<{ line: Line, destination: string, index: number, style: string, fontSize: number, fallbackStyle: string }> = (props) => { + + const businessDataContext: BusinessDataStore | undefined = useContext(BusinessDataContext); + if (businessDataContext === undefined) + return
; + + const { getDestinationPassages } = businessDataContext; + + const [dateNow] = createDateNow(10000); + + const transition: AnimationOptions = { duration: 3, repeat: Infinity }; + + return (() => { + const passage = getDestinationPassages(props.line.id, props.destination)[props.index]; + + const refTs = passage !== undefined ? (passage.expectedDepartTs !== null ? passage.expectedDepartTs : passage.expectedArrivalTs) : 0; + const ttwSec = refTs - (getTime(dateNow()) / 1000); + const isApproaching = ttwSec <= 60; + + return ( + > +
+ + + {Math.floor(ttwSec / 60)} min + + +
+
+ ); + }); +} + /* TODO: Manage end of service */ -const DestinationPassages: VoidComponent<{ passages: Passage[], line: Line, destination: string }> = (props) => { +const DestinationPassages: VoidComponent<{ line: Line, destination: string }> = (props) => { /* TODO: Find where to get data to compute traffic status. */ const trafficStatusColor = new Map([ @@ -62,10 +75,6 @@ const DestinationPassages: VoidComponent<{ passages: Passage[], line: Line, dest [TrafficStatus.BYPASSED, "#ffffff"] ]); - const passagesLength = props.passages.length; - const firstPassage = passagesLength > 0 ? props.passages[0] : undefined; - const secondPassage = passagesLength > 1 ? props.passages[1] : undefined; - // TODO: Manage traffic status // const trafficStatusStyle = { fill: trafficStatusColor.get(props.line.trafficStatus) }; const trafficStatusStyle = { fill: trafficStatusColor.get(TrafficStatus.UNKNOWN) }; @@ -88,17 +97,15 @@ const DestinationPassages: VoidComponent<{ passages: Passage[], line: Line, dest
- > - - - > - - + +
); } -export type PassagesPanelComponentProps = ParentProps & { passages: Passages, show: boolean }; +export type PassagesPanelComponentProps = ParentProps & { stopId: number, lineIds: string[], show: boolean }; export type PassagesPanelComponent = ParentComponent; export const PassagesPanel: PassagesPanelComponent = (props) => { @@ -106,29 +113,23 @@ export const PassagesPanel: PassagesPanelComponent = (props) => { if (businessDataContext === undefined) return
; - const { getLine } = businessDataContext; + const { getLine, getLineDestinations } = businessDataContext; const getLines = async (lineIds: string[]): Promise => { const lines = await Promise.all[]>(lineIds.map((lineId) => getLine(lineId))); return lines; } - - const [lineIds, setLinesIds] = createSignal([]); - const [lines] = createResource(lineIds, getLines); - - createEffect(async () => { - setLinesIds(Object.keys(props.passages)); - }); + const [lines] = createResource(props.lineIds, getLines); return (
{(line) => - - + + {(destination) => - + }