Files
flights_web/src/shared/hooks/useLiveFlights.ts
T
gnezim c8d0caa9cf Fix five console-level issues surfaced by live-deploy Playwright audit
1. FlightsMap tiles didn't render: MapCanvas inline height:100% resolved
   to 0 against min-height parents. Hand sizing to consumer CSS so
   .flights-map-start__map height:500px wins.

2. FlightsMap /map/api/tile/{z}/{x}/{y}.jpeg requests fell through to
   Modern.js SSR (HTML body). Dev proxy now forwards /map/* to the
   test env via curl with image headers and binary-safe piping.

3. PopularRequestsPanel duplicate React key (Route-SVO-LED appears
   twice in upstream). Suffix the key with the visible index.

4. OnlineBoardDetailsPage /onlineboard/details 400. Upstream expects
   an ISO datetime (yyyy-MM-DDTHH:mm:ss), matching Angular's
   ApiFormatterService.formatDate. Append T00:00:00.

5. Browser-level SignalR CORS errors on every details page: the
   default SIGNALR_HUB_URL pointed at an unreachable placeholder.
   Default to empty + skip the connection in useLiveFlights when
   blank. Also configureLogging(LogLevel.None) so SignalR stops
   writing its own negotiation failures to console. Live updates
   re-enable by setting SIGNALR_HUB_URL on a deployment.
2026-04-17 21:55:44 +03:00

72 lines
2.1 KiB
TypeScript

import { useState, useEffect, useRef } from "react";
import {
getSharedConnection,
type ConnectionStatus,
} from "../signalr/connection.js";
export interface UseLiveFlightsConfig<TParams> {
hubUrl: string;
channelKey: (params: TParams) => string;
}
export interface UseLiveFlightsResult<TData> {
data: TData[];
connectionStatus: ConnectionStatus;
}
/**
* Generic SSR-safe hook for live flight data via SignalR.
*
* During SSR (`typeof window === "undefined"`), returns the initial data
* without touching SignalR. On the client, subscribes to a SignalR channel
* and merges incoming messages into state.
*/
export function useLiveFlights<TParams, TData>(
params: TParams,
initialData: TData[],
config: UseLiveFlightsConfig<TParams>,
): UseLiveFlightsResult<TData> {
const [data, setData] = useState<TData[]>(initialData);
const [connectionStatus, setConnectionStatus] =
useState<ConnectionStatus>("idle");
// Keep a stable reference to initialData for the SSR short-circuit
const initialDataRef = useRef(initialData);
initialDataRef.current = initialData;
const isClient = typeof window !== "undefined";
useEffect(() => {
if (!isClient) return;
// Blank hubUrl = live updates disabled for this deployment. Skip the
// connection entirely; a deployment that wants live updates sets a
// real SIGNALR_HUB_URL (same-origin or CORS-enabled).
if (!config.hubUrl) return;
const channel = config.channelKey(params);
const connection = getSharedConnection({ hubUrl: config.hubUrl });
// Sync current status
setConnectionStatus(connection.status);
const unsubStatus = connection.onStatusChange((status) => {
setConnectionStatus(status);
});
const unsubChannel = connection.subscribe(channel, (message) => {
setData(message as TData[]);
});
return () => {
unsubChannel();
unsubStatus();
};
}, [isClient, config.hubUrl, config.channelKey, params]);
if (!isClient) {
return { data: initialData, connectionStatus: "idle" };
}
return { data, connectionStatus };
}