diff --git a/src/features/online-board/components/OnlineBoardDetailsPage.tsx b/src/features/online-board/components/OnlineBoardDetailsPage.tsx index 0937cc75..f0133f33 100644 --- a/src/features/online-board/components/OnlineBoardDetailsPage.tsx +++ b/src/features/online-board/components/OnlineBoardDetailsPage.tsx @@ -401,7 +401,7 @@ export const OnlineBoardDetailsPage: FC = ({ // Live updates via SignalR const { flight: liveFlight, connectionStatus } = useLiveFlightDetails( - flight?.flightId ?? flightId, + flight?.flightId ?? null, flight, refresh, ); diff --git a/src/features/online-board/hooks/useLiveFlightDetails.test.ts b/src/features/online-board/hooks/useLiveFlightDetails.test.ts index 739709ba..811ee05e 100644 --- a/src/features/online-board/hooks/useLiveFlightDetails.test.ts +++ b/src/features/online-board/hooks/useLiveFlightDetails.test.ts @@ -240,6 +240,23 @@ describe("useLiveFlightDetails", () => { expect(result.current.flight).toBeNull(); }); + it("does not subscribe before the API flight id is available", async () => { + const { hub, conn } = installMockHub(HUB_URL); + const subscribeSpy = vi.spyOn(conn, "subscribe"); + + const { result } = renderHook(() => + useLiveFlightDetails(null, null), + ); + + await act(async () => { + await vi.runAllTimersAsync(); + }); + + expect(result.current.connectionStatus).toBe("idle"); + expect(subscribeSpy).not.toHaveBeenCalled(); + expect(hub.invoke).not.toHaveBeenCalled(); + }); + it("subscribes to Angular TrackerHub refresh and invokes Subscribe", async () => { const { hub } = installMockHub(HUB_URL); const id: IParsedFlightId = { diff --git a/src/features/online-board/hooks/useLiveFlightDetails.ts b/src/features/online-board/hooks/useLiveFlightDetails.ts index b74b7369..c4b28435 100644 --- a/src/features/online-board/hooks/useLiveFlightDetails.ts +++ b/src/features/online-board/hooks/useLiveFlightDetails.ts @@ -34,6 +34,13 @@ export interface UseLiveFlightDetailsResult { type LiveFlightId = IParsedFlightId | IFlightId; +const DISABLED_ID: LiveFlightId = { + carrier: "", + flightNumber: "", + suffix: "", + date: "", +}; + export function buildFlightChannelKey(id: LiveFlightId): string { return `flight:${id.carrier}${id.flightNumber}${id.suffix ?? ""}@${id.date}`; } @@ -48,13 +55,15 @@ export function buildTrackerFlightSubscriptionKey(id: LiveFlightId): string { // --------------------------------------------------------------------------- export function useLiveFlightDetails( - id: LiveFlightId, + id: LiveFlightId | null, initialFlight: ISimpleFlight | null, onRefresh?: () => void, ): UseLiveFlightDetailsResult { + const env = getEnv(); + const effectiveId = id ?? DISABLED_ID; const config = useMemo>( () => ({ - hubUrl: getEnv().SIGNALR_HUB_URL, + hubUrl: id ? env.SIGNALR_HUB_URL : "", channelKey: buildFlightChannelKey, subscription: { eventName: "Refresh", @@ -67,7 +76,7 @@ export function useLiveFlightDetails( return undefined; }, }), - [onRefresh], + [env.SIGNALR_HUB_URL, id, onRefresh], ); // useLiveFlights expects an array — wrap/unwrap the single flight @@ -79,7 +88,7 @@ export function useLiveFlightDetails( const { data, connectionStatus } = useLiveFlights< LiveFlightId, ISimpleFlight - >(id, initialData, config); + >(effectiveId, initialData, config); const flight = data[0] ?? null;