aboutsummaryrefslogtreecommitdiff
path: root/src/frontend/app/contexts
diff options
context:
space:
mode:
Diffstat (limited to 'src/frontend/app/contexts')
-rw-r--r--src/frontend/app/contexts/JourneyContext.tsx84
1 files changed, 84 insertions, 0 deletions
diff --git a/src/frontend/app/contexts/JourneyContext.tsx b/src/frontend/app/contexts/JourneyContext.tsx
new file mode 100644
index 0000000..f513aa8
--- /dev/null
+++ b/src/frontend/app/contexts/JourneyContext.tsx
@@ -0,0 +1,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;
+}