102 lines
3.0 KiB
JavaScript
102 lines
3.0 KiB
JavaScript
import React, { createContext, useContext, useEffect, useRef, useState } from 'react';
|
|
|
|
const BackendContext = createContext(null);
|
|
|
|
export function useBackend() {
|
|
return useContext(BackendContext);
|
|
}
|
|
|
|
export function BackendProvider({ children }) {
|
|
const API_BASE = process.env.REACT_APP_API_BASE || '';
|
|
const [backendOnline, setBackendOnline] = useState(false);
|
|
const [checking, setChecking] = useState(true);
|
|
const esRef = useRef(null);
|
|
const eventTargetRef = useRef(new EventTarget());
|
|
|
|
useEffect(() => {
|
|
let mounted = true;
|
|
const check = async () => {
|
|
if (!mounted) return;
|
|
setChecking(true);
|
|
try {
|
|
const resp = await fetch(`${API_BASE}/api/servers/health`);
|
|
if (!mounted) return;
|
|
setBackendOnline(!!(resp && resp.ok));
|
|
} catch (e) {
|
|
if (!mounted) return;
|
|
setBackendOnline(false);
|
|
} finally {
|
|
if (mounted) setChecking(false);
|
|
}
|
|
};
|
|
check();
|
|
const iv = setInterval(check, 5000);
|
|
return () => { mounted = false; clearInterval(iv); };
|
|
}, [API_BASE]);
|
|
|
|
// Single shared EventSource forwarded into a DOM EventTarget
|
|
useEffect(() => {
|
|
if (typeof window === 'undefined' || typeof EventSource === 'undefined') return;
|
|
const url = `${API_BASE}/api/events`;
|
|
let es = null;
|
|
try {
|
|
es = new EventSource(url);
|
|
esRef.current = es;
|
|
} catch (err) {
|
|
// silently ignore
|
|
return;
|
|
}
|
|
|
|
const forward = (type) => (e) => {
|
|
try {
|
|
const evt = new CustomEvent(type, { detail: e.data ? JSON.parse(e.data) : null });
|
|
eventTargetRef.current.dispatchEvent(evt);
|
|
} catch (err) {
|
|
// ignore parse errors
|
|
}
|
|
};
|
|
|
|
es.addEventListener('connected', forward('connected'));
|
|
es.addEventListener('commandToggle', forward('commandToggle'));
|
|
es.addEventListener('twitchUsersUpdate', forward('twitchUsersUpdate'));
|
|
es.addEventListener('liveNotificationsUpdate', forward('liveNotificationsUpdate'));
|
|
es.addEventListener('adminLogAdded', forward('adminLogAdded'));
|
|
es.addEventListener('adminLogDeleted', forward('adminLogDeleted'));
|
|
es.addEventListener('adminLogsCleared', forward('adminLogsCleared'));
|
|
|
|
es.onerror = () => {
|
|
// Let consumers react to backendOnline state changes instead of surfacing connection errors
|
|
};
|
|
|
|
return () => { try { es && es.close(); } catch (e) {} };
|
|
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
|
|
const forceCheck = async () => {
|
|
const API_BASE2 = process.env.REACT_APP_API_BASE || '';
|
|
try {
|
|
setChecking(true);
|
|
const resp = await fetch(`${API_BASE2}/api/servers/health`);
|
|
setBackendOnline(!!(resp && resp.ok));
|
|
} catch (e) {
|
|
setBackendOnline(false);
|
|
} finally {
|
|
setChecking(false);
|
|
}
|
|
};
|
|
|
|
const value = {
|
|
backendOnline,
|
|
checking,
|
|
eventTarget: eventTargetRef.current,
|
|
forceCheck,
|
|
};
|
|
|
|
return (
|
|
<BackendContext.Provider value={value}>
|
|
{children}
|
|
</BackendContext.Provider>
|
|
);
|
|
}
|
|
|
|
export default BackendContext;
|