aboutsummaryrefslogtreecommitdiff
path: root/src/frontend/app/contexts/JourneyContext.tsx
blob: f513aa88c6c6cb664be32b17622a75d7c8ad2ba8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import {
  createContext,
  useCallback,
  useContext,
  useRef,
  useState,
  type ReactNode,
} from "react";

export interface ActiveJourney {
  tripId: string;
  stopId: string;
  stopName: string;
  routeShortName: string;
  routeColour: string;
  routeTextColour: string;
  headsignDestination: string | null;
  /** Minutes remaining when tracking was started (for display context) */
  initialMinutes: number;
  /** Send notification when this many minutes remain (default: 2) */
  notifyAtMinutes: number;
  /** Whether the "approaching" notification has already been sent */
  hasNotified: boolean;
}

interface JourneyContextValue {
  activeJourney: ActiveJourney | null;
  startJourney: (
    journey: Omit<ActiveJourney, "hasNotified">
  ) => void;
  stopJourney: () => void;
  markNotified: () => void;
}

const JourneyContext = createContext<JourneyContextValue | null>(null);

export function JourneyProvider({ children }: { children: ReactNode }) {
  const [activeJourney, setActiveJourney] = useState<ActiveJourney | null>(
    null
  );
  const notificationRef = useRef<Notification | null>(null);

  const startJourney = useCallback(
    (journey: Omit<ActiveJourney, "hasNotified">) => {
      // Close any existing notification
      if (notificationRef.current) {
        notificationRef.current.close();
        notificationRef.current = null;
      }
      setActiveJourney({ ...journey, hasNotified: false });
    },
    []
  );

  const stopJourney = useCallback(() => {
    if (notificationRef.current) {
      notificationRef.current.close();
      notificationRef.current = null;
    }
    setActiveJourney(null);
  }, []);

  const markNotified = useCallback(() => {
    setActiveJourney((prev) =>
      prev ? { ...prev, hasNotified: true } : null
    );
  }, []);

  return (
    <JourneyContext.Provider
      value={{ activeJourney, startJourney, stopJourney, markNotified }}
    >
      {children}
    </JourneyContext.Provider>
  );
}

export function useJourney() {
  const ctx = useContext(JourneyContext);
  if (!ctx) {
    throw new Error("useJourney must be used within a JourneyProvider");
  }
  return ctx;
}