mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 01:21:20 -04:00
117 lines
3.2 KiB
JavaScript
117 lines
3.2 KiB
JavaScript
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
import { useSocket } from '../context/SocketContext.jsx';
|
|
|
|
function normalizeEntry(entry) {
|
|
if (!entry) return null;
|
|
if (typeof entry === 'string') {
|
|
const id = String(entry).trim();
|
|
if (!id) return null;
|
|
return { type: 'rover', id, key: id };
|
|
}
|
|
if (typeof entry === 'object') {
|
|
if (entry.type && entry.id) {
|
|
const id = String(entry.id);
|
|
let key = entry.key;
|
|
if (!key) {
|
|
key = entry.type === 'room' ? `room:${id}` : id;
|
|
}
|
|
return {
|
|
type: entry.type,
|
|
id,
|
|
key,
|
|
};
|
|
}
|
|
if (entry.roverId) {
|
|
const id = String(entry.roverId);
|
|
return { type: 'rover', id, key: entry.key || id };
|
|
}
|
|
if (entry.roomCameraId) {
|
|
const id = String(entry.roomCameraId);
|
|
return { type: 'room', id, key: entry.key || `room:${id}` };
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function dedupeEntries(entries = []) {
|
|
const seen = new Set();
|
|
const unique = [];
|
|
entries.forEach((entry) => {
|
|
if (!entry?.key || seen.has(entry.key)) {
|
|
return;
|
|
}
|
|
seen.add(entry.key);
|
|
unique.push(entry);
|
|
});
|
|
return unique.sort((a, b) => a.key.localeCompare(b.key));
|
|
}
|
|
|
|
export function useVideoRequests(sourceList = [], options = {}) {
|
|
const socket = useSocket();
|
|
const { enabled = true, version = null } = options;
|
|
const [sources, setSources] = useState({});
|
|
const normalizedEntries = useMemo(() => {
|
|
if (!Array.isArray(sourceList)) {
|
|
return [];
|
|
}
|
|
return dedupeEntries(sourceList.map(normalizeEntry).filter(Boolean));
|
|
}, [sourceList]);
|
|
const normalizedKey = useMemo(() => {
|
|
const base = normalizedEntries.map((entry) => `${entry.type}:${entry.id}:${entry.key}`).join('|');
|
|
return version ? `${base}|v:${version}` : base;
|
|
}, [normalizedEntries, version]);
|
|
const entriesRef = useRef([]);
|
|
const [connectionNonce, setConnectionNonce] = useState(0);
|
|
|
|
useEffect(() => {
|
|
if (!socket) return undefined;
|
|
const handleConnect = () => setConnectionNonce((prev) => prev + 1);
|
|
socket.on('connect', handleConnect);
|
|
return () => {
|
|
socket.off('connect', handleConnect);
|
|
};
|
|
}, [socket]);
|
|
|
|
useEffect(() => {
|
|
entriesRef.current = normalizedEntries;
|
|
}, [normalizedEntries]);
|
|
|
|
useEffect(() => {
|
|
if (!normalizedKey || !enabled) {
|
|
setSources({});
|
|
return undefined;
|
|
}
|
|
const entries = entriesRef.current;
|
|
let cancelled = false;
|
|
|
|
function requestEntry(entry) {
|
|
const payload = entry.type === 'room' ? { roomCameraId: entry.id } : { roverId: entry.id };
|
|
socket.emit('video:request', payload, (resp = {}) => {
|
|
if (cancelled) return;
|
|
setSources((prev) => ({ ...prev, [entry.key]: resp }));
|
|
});
|
|
}
|
|
|
|
entries.forEach((entry) => {
|
|
requestEntry(entry);
|
|
});
|
|
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, [socket, normalizedKey, enabled, connectionNonce]);
|
|
|
|
const filtered = useMemo(() => {
|
|
if (!normalizedKey || !enabled) return {};
|
|
const next = {};
|
|
normalizedEntries.forEach((entry) => {
|
|
if (sources[entry.key]) {
|
|
next[entry.key] = sources[entry.key];
|
|
}
|
|
});
|
|
return next;
|
|
}, [normalizedKey, sources, normalizedEntries]);
|
|
|
|
return filtered;
|
|
}
|