c8d0caa9cf
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.
72 lines
2.1 KiB
TypeScript
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 };
|
|
}
|