Skip to content
Snippets Groups Projects
FahrplanPanel.tsx 5.22 KiB
Newer Older
  • Learn to ignore specific revisions
  • import React, {useEffect, useState} from 'react';
    
    import PanelWrapper from "../../meta/PanelWrapper";
    import PlanElement from "./components/PlanElement";
    import PanelTitle from "../../meta/PanelTitle";
    import PanelContent from "../../meta/PanelContent";
    
    import {StationResponse} from "./types/vrrfAPI";
    import {Warning} from "phosphor-react";
    
    export type FahrplanPanelDefinition = {
      stops: string[],
      filter: {
        types?: string[],
        destinations?: string[]
      }
    }
    
    type Route = {
      uid: string,
      identifier: string,
      heading: string,
      stops: {
        name: string,
        arrival: Date,
        delay?: number
      }[]
    }
    
    const FahrplanPanel = (props: {definition: FahrplanPanelDefinition}) => {
      const [routes, setRoutes] = useState<Route[]>([]);
    
      useEffect(() => {
        const update = async () => {
          const departures = (await Promise.all(props.definition.stops.map(getStopData)))
            .map(d => d.raw)
            .flat();
    
          let newRoutes: Route[] = [];
    
          // Determine stop data from the departures
          for(let departure of departures) {
            // First throw away all data that belongs to a filtered category
            if((props.definition.filter.types ?? []).includes(departure.type)) {
              continue;
            }
    
            // Find existing route with same uid
            const existing_ind = newRoutes.findIndex(r => r.uid === departure.lineref.identifier)
    
            // Pre-compute values that will be needed regardless
            const delay = stringToDelay(departure.delay);
            const arrival = processArrival(departure.sched_date, departure.time);
            console.log(arrival)
    
            if(existing_ind === -1) {
              // If it does not exist, create a new route
              newRoutes.push({
                uid: departure.key,
                heading: departure.lineref.direction,
                identifier: departure.line,
                stops: [
                  {
                    name: departure.internal.stop,
                    arrival,
                    delay
                  }
                ]
              })
            } else {
              // If it doesn't, just add a stop to the existing route
              newRoutes[existing_ind].stops.push({
                name: departure.internal.stop,
                arrival,
                delay: stringToDelay(departure.delay)
              })
            }
          }
    
          // Sort the output
    
          // Write to the display
          setRoutes(newRoutes);
        }
    
        update();
        const interval = setInterval(update, 2 * 60 * 1000);
    
        return () => {
          clearInterval(interval);
        }
      }, []);
    
    
      return (
        <PanelWrapper>
          <PanelTitle title={"ÖPNV Monitor"}/>
    
          <PanelContent className={"flex flex-col"}>
            <div className={"flex-1 flex flex-col gap-3"}>
              {routes.map((route) => (
                <PlanElement
                  key={route.uid}
                  tripId={route.uid}
                  trainIdentifier={route.identifier}
                  trainHeading={route.heading}
                  stops={route.stops}
                />
              ))}
    
              {routes.length === 0 && (
                <div className={"flex-1 flex justify-center items-center"}>
                  <div className={"mb-10 flex flex-col items-center"}>
                    <Warning size={48} className={"mb-3"}/>
                    <p className={"max-w-xs text-center text-zinc-400"}>
                      Aktuell sind keine Abfahrtsdaten verfügbar.
                    </p>
                  </div>
                </div>
              )}
    
            </div>
          </PanelContent>
        </PanelWrapper>
    
    
      );
    };
    
    export default FahrplanPanel;
    
    
    async function getStopData(stop: string): Promise<StationResponse> {
      const request = await fetch(`https://vrrf.finalrewind.org/${encodeURIComponent(stop)}.json`);
      if(!request.ok) {
        throw new Error("Converting stop did not work");
      }
      const data = await request.json();
    
      if(data.error) {
        console.warn("Stop data for", stop, "could not be fetched");
      }
    
      // Add internal reference data
      data.raw = data.raw.map((r: any) => ({
        ...r,
        internal: {
          stop
        }
      }))
    
      console.log(data);
    
      return data as StationResponse;
    }
    
    function stringToDelay(input: string): number | undefined {
      try {
        const delay = parseInt(input);
        if(delay === 0) {
          return undefined;
        }
        return delay;
      } catch (e) {
        console.warn("While parsing delay, the string was not interpretable", input);
        return undefined;
      }
    }
    
    function processArrival(date: string, time: string): Date {
      const d_parts = date.split(".");
    
      console.log(date, time, "to", `${d_parts[2]}-${d_parts[1]}-${d_parts[0]} ${time}`);
      return new Date(`${d_parts[2]}-${d_parts[1]}-${d_parts[0]} ${time}`);
    }
    
    // function sortData(data: any) {
    //   for (var i = 0; i < data.length; ++i) {
    //     data[i]['stops'] = data[i]['stops'].sort(sortFn);
    //     data[i]['timeValue'] = data[i]['stops'][0]['timeValue'];
    //   }
    //   return data.sort(sortFn);
    // }
    //
    // function sortFn(a: any, b: any) {
    //   return a['timeValue'] - b['timeValue'];
    // }
    //
    // function calcDateValue(_year: string, _month: string, _day: string, _hour: string, _minute: string): number {
    //   const year = parseInt(_year) * 12 * 31 * 24 * 60;
    //   const month = parseInt(_month) * 31 * 24 * 60;
    //   const day = parseInt(_day) * 24 * 60;
    //   const hour = parseInt(_hour) * 60;
    //   const minute = parseInt(_minute);
    //   return year+month+day+hour+minute;
    // }