From f3f01f9717329937b3b885fc390ece589c64e356 Mon Sep 17 00:00:00 2001 From: Daniel Kluge Date: Mon, 3 Jan 2022 21:56:20 +0100 Subject: [PATCH] DVB Container --- src/components/DVB.tsx | 73 ++++++++++++++++++++++++++++ src/components/News.tsx | 3 +- src/lib/interfaces.ts | 20 ++++++++ src/lib/utils.ts | 4 ++ src/pages/index.tsx | 2 + src/styles/containers/DVB.module.css | 50 +++++++++++++++++++ 6 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 src/components/DVB.tsx create mode 100644 src/styles/containers/DVB.module.css diff --git a/src/components/DVB.tsx b/src/components/DVB.tsx new file mode 100644 index 0000000..9c05fd0 --- /dev/null +++ b/src/components/DVB.tsx @@ -0,0 +1,73 @@ +import * as React from "react"; +import type { Departure } from "../lib/interfaces"; +import { minuteDiff } from "../lib/utils"; +import * as styles from "../styles/containers/DVB.module.css"; + +const DVB_REFRESH_INTERVAL = 30 * 1000 + +const DVB = ({ stopId }: { stopId: number }) => { + const [departuresHead, setDeparturesHead] = React.useState("") + const [departuresTable, setDeparturesTable] = React.useState([]) + + React.useEffect(() => { + //pullDepartures(); + // TODO + //const dvbInterval = setInterval(pullDepartures, DVB_REFRESH_INTERVAL); + + //return () => clearInterval(dvbInterval); + }, []) + + const processDepatures = (departures: Departure[]) => { + const depTable = []; + + departures.forEach((departure, index) => { + const realTime = departure.RealTime ? new Date(parseInt(departure.RealTime.replace(/\/Date\(/g, "").replace(/\-.*$/g, ""))) : undefined; + const scheduledTime = new Date(parseInt(departure.ScheduledTime.replace(/\/Date\(/g, "").replace(/\-.*$/g, ""))); + const timeToDisplay = realTime || scheduledTime; + const timeDelay = realTime ? minuteDiff(realTime, scheduledTime) : 0; + + depTable.push( + + {departure.LineName} + {departure.Direction} + {timeToDisplay.getHours()}:{timeToDisplay.getMinutes().toString().padStart(2, "0")} + {minuteDiff(timeToDisplay, new Date())} + {timeDelay > 0 ? "+" : ""}{timeDelay !== 0 ? timeDelay : false} + + ); + }); + + setDeparturesTable(depTable); + } + + const pullDepartures = async () => { + const time = new Date(); + time.setMinutes(time.getMinutes() + 5) + const response = await fetch("https://webapi.vvo-online.de/dm", { + method: "POST", + body: JSON.stringify({ + stopid: stopId, + limit: 5, + time: time.toUTCString() + }), + headers: { + "Content-Type": "application/json" + } + }); + const data = await response.json(); + if (data.Name !== departuresHead) setDeparturesHead(data.Name); + console.log(data) + processDepatures(data.Departures); + } + + return (
+ + + + {departuresTable} + +
+
) +} + +export default DVB; \ No newline at end of file diff --git a/src/components/News.tsx b/src/components/News.tsx index 12e2858..14b6071 100644 --- a/src/components/News.tsx +++ b/src/components/News.tsx @@ -1,6 +1,7 @@ import * as React from "react"; import { XMLParser } from "fast-xml-parser"; import * as styles from "../styles/containers/News.module.css" +import type { News as NewsType, PostillonNews } from "../lib/interfaces"; const NEWS_REFRESH_INTERVAL = 15 * 60 * 1000; @@ -14,7 +15,7 @@ const News = () => { return () => clearInterval(newsInterval); }, []) - const processNews = (news: {title: string; updated: string;}[], postillon: {title: string; pubDate: string; categories: string[]}[]) => { + const processNews = (news: NewsType[], postillon: PostillonNews[]) => { const newsTable = [] for (const n of news) { diff --git a/src/lib/interfaces.ts b/src/lib/interfaces.ts index 37eb94f..f3c6666 100644 --- a/src/lib/interfaces.ts +++ b/src/lib/interfaces.ts @@ -30,4 +30,24 @@ export interface WeatherInfo { export interface Event { start: { dateTime: string; date?: string; }; summary: string; +} + +export interface News { + title: string; + updated: string; +} + +export interface PostillonNews { + title: string; + pubDate: string; + categories: string[]; +} + +export interface Departure { + Direction: string; + LineName: string; + RealTime?: string; + ScheduledTime: string; + State?: "Delayed" | "InTime" | "Canceled"; + CancelReasons?: string[]; } \ No newline at end of file diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 6d94904..62d578e 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -27,4 +27,8 @@ export function monthToString(month: number) { case 11: return "Dezember"; default: return; } +} + +export function minuteDiff(later: Date, before: Date) { + return Math.floor((later.getTime() - before.getTime()) / 60000); } \ No newline at end of file diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 62ce676..ecf5f80 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,6 +1,7 @@ import * as React from "react" import secrets from "../../secrets.json" import Calendar from "../components/Calendar"; +import DVB from "../components/DVB"; import News from "../components/News"; import WeatherAndTimeContainer from "../components/WeatherAndTime" import WeatherRadar from "../components/WeatherRadar"; @@ -36,6 +37,7 @@ const IndexPage = () => { + ) } diff --git a/src/styles/containers/DVB.module.css b/src/styles/containers/DVB.module.css new file mode 100644 index 0000000..fa3855e --- /dev/null +++ b/src/styles/containers/DVB.module.css @@ -0,0 +1,50 @@ +.container { + grid-area: dvb; + display: flex; + flex-direction: column; +} + +.container table { + height: 100%; +} + +.departureHeader { + font-weight: bold; + font-size: 110%; +} + +.departure { + font-size: 150%; + font-weight: bold; + margin: auto 0; +} + +.departure td { + padding: 2px 10px; +} + +.departure td:nth-child(1) { + padding-left: 20px !important; +} + +.departure td:nth-child(2) { + width: 100%; +} + +.departure td:nth-child(3), .departure td:nth-child(4) { + text-align: right; +} + +.delay, .beforeTime { + padding-left: 0 !important; + font-size: 70%; + font-weight: normal; +} + +.delay { + color: #ff0000; +} + +.beforeTime { + color: #0000ff; +} \ No newline at end of file