feat(sprint-3): refresh responsive UlHub portal
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import type { LiveStatus, MapPoint } from '../components/map/types';
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import type { LiveStatus, MapPoint } from "../components/map/types";
|
||||
|
||||
export type JobStreamState = "connecting" | "live" | "offline" | "error";
|
||||
|
||||
// Subscribes to live points + live position status for a job over the
|
||||
// backend WebSocket. Reconnects with capped exponential backoff; resubscribes
|
||||
@@ -9,6 +11,8 @@ export function useJobStream(
|
||||
onPoints: (points: MapPoint[]) => void,
|
||||
onStatus?: (status: LiveStatus) => void,
|
||||
) {
|
||||
const [connectionState, setConnectionState] =
|
||||
useState<JobStreamState>("connecting");
|
||||
const pointsRef = useRef(onPoints);
|
||||
pointsRef.current = onPoints;
|
||||
const statusRef = useRef(onStatus);
|
||||
@@ -16,6 +20,7 @@ export function useJobStream(
|
||||
|
||||
useEffect(() => {
|
||||
if (!jobId) {
|
||||
setConnectionState("offline");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -25,12 +30,17 @@ export function useJobStream(
|
||||
let timer: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
const connect = () => {
|
||||
const proto = window.location.protocol === 'https:' ? 'wss' : 'ws';
|
||||
let socketErrored = false;
|
||||
setConnectionState(attempt === 0 ? "connecting" : "offline");
|
||||
const proto = window.location.protocol === "https:" ? "wss" : "ws";
|
||||
socket = new WebSocket(`${proto}://${window.location.host}/api/ws`);
|
||||
|
||||
socket.onopen = () => {
|
||||
attempt = 0;
|
||||
socket?.send(JSON.stringify({ type: 'subscribe', channel: `job:${jobId}` }));
|
||||
setConnectionState("live");
|
||||
socket?.send(
|
||||
JSON.stringify({ type: "subscribe", channel: `job:${jobId}` }),
|
||||
);
|
||||
};
|
||||
|
||||
socket.onmessage = (event) => {
|
||||
@@ -39,9 +49,9 @@ export function useJobStream(
|
||||
if (msg.jobId !== jobId) {
|
||||
return;
|
||||
}
|
||||
if (msg.type === 'points') {
|
||||
if (msg.type === "points") {
|
||||
pointsRef.current(msg.points);
|
||||
} else if (msg.type === 'status') {
|
||||
} else if (msg.type === "status") {
|
||||
statusRef.current?.(msg as LiveStatus);
|
||||
}
|
||||
} catch {
|
||||
@@ -49,10 +59,18 @@ export function useJobStream(
|
||||
}
|
||||
};
|
||||
|
||||
socket.onerror = () => {
|
||||
socketErrored = true;
|
||||
setConnectionState("error");
|
||||
};
|
||||
|
||||
socket.onclose = () => {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
if (!socketErrored) {
|
||||
setConnectionState("offline");
|
||||
}
|
||||
attempt += 1;
|
||||
const delay = Math.min(1000 * 2 ** attempt, 15000);
|
||||
timer = setTimeout(connect, delay);
|
||||
@@ -69,4 +87,6 @@ export function useJobStream(
|
||||
socket?.close();
|
||||
};
|
||||
}, [jobId]);
|
||||
|
||||
return connectionState;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user