diff --git a/react-openapi/index.ts b/react-openapi/index.ts index d75ee63..3b5f543 100644 --- a/react-openapi/index.ts +++ b/react-openapi/index.ts @@ -7,5 +7,6 @@ export { FormFieldRenderer } from "./src/components/fields/FormFieldRenderer"; export { SseStreamView } from "./src/components/SseStreamView"; export { SseConnectionStatus } from "./src/components/SseConnectionStatus"; export { getApi } from "./src/hooks/useApi"; +export { useItemSse } from "./src/hooks/useItemSse"; export type { FilterComponentProps } from "./src/context/useResource"; export type { SpecConfiguration, ResourceConfig, FieldConfig, FKFieldConfig, ResourceRelationship } from "./src/types"; diff --git a/react-openapi/src/hooks/useItemSse.ts b/react-openapi/src/hooks/useItemSse.ts new file mode 100644 index 0000000..606c2b1 --- /dev/null +++ b/react-openapi/src/hooks/useItemSse.ts @@ -0,0 +1,58 @@ +import { useEffect, useRef, useState, useCallback } from "react"; +import { getApi } from "./useApi"; + +interface ItemSseHandlers { + onEvent: (data: any) => void; + onOpen?: () => void; + onError?: (evt: Event) => void; +} + +interface ItemSseResult { + connected: boolean; + events: any[]; +} + +export function useItemSse(url: string | null, handlers: ItemSseHandlers): ItemSseResult { + const [connected, setConnected] = useState(false); + const [events, setEvents] = useState([]); + const handlersRef = useRef(handlers); + handlersRef.current = handlers; + + useEffect(() => { + if (!url) return; + + const api = getApi(); + const baseUrl = (api.defaults.baseURL ?? "").replace(/\/+$/, ""); + const fullUrl = baseUrl + url; + const es = new EventSource(fullUrl); + + es.onopen = () => { + setConnected(true); + handlersRef.current.onOpen?.(); + }; + + es.onmessage = (e) => { + try { + const data = JSON.parse(e.data); + setEvents((prev) => [...prev, data]); + handlersRef.current.onEvent(data); + } catch { + // ignore malformed JSON + } + }; + + es.onerror = (e) => { + setConnected(false); + handlersRef.current.onError?.(e); + }; + + return () => { + es.close(); + setConnected(false); + }; + }, [url]); + + const clearEvents = useCallback(() => setEvents([]), []); + + return { connected, events }; +}