chore: ticket section implement sse
This commit is contained in:
142
src/hooks/sse/useSseWithToken.ts
Normal file
142
src/hooks/sse/useSseWithToken.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
'use client';
|
||||
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
|
||||
import {
|
||||
sseService,
|
||||
type SseConnection,
|
||||
type SseEventMap,
|
||||
} from '@/services/sse';
|
||||
|
||||
const DEFAULT_RETRY_DELAY_MS = 2500;
|
||||
const EMPTY_RECONNECT_EVENTS: ReadonlyArray<PropertyKey> = [];
|
||||
|
||||
interface SseToken {
|
||||
sse_token: string;
|
||||
}
|
||||
|
||||
interface UseSseWithTokenOptions<TEvents extends Record<string, unknown>> {
|
||||
enabled?: boolean;
|
||||
getToken: () => Promise<SseToken>;
|
||||
getPath: (token: string) => string;
|
||||
events: SseEventMap<TEvents>;
|
||||
retryDelayMs?: number;
|
||||
onConnectionError?: () => void;
|
||||
reconnectOnEvents?: ReadonlyArray<keyof TEvents>;
|
||||
shouldStop?: (
|
||||
eventName: keyof TEvents,
|
||||
event: TEvents[keyof TEvents],
|
||||
) => boolean;
|
||||
}
|
||||
|
||||
export function useSseWithToken<TEvents extends Record<string, unknown>>({
|
||||
enabled = true,
|
||||
getToken,
|
||||
getPath,
|
||||
events,
|
||||
retryDelayMs = DEFAULT_RETRY_DELAY_MS,
|
||||
onConnectionError,
|
||||
reconnectOnEvents = EMPTY_RECONNECT_EVENTS as ReadonlyArray<keyof TEvents>,
|
||||
shouldStop,
|
||||
}: UseSseWithTokenOptions<TEvents>) {
|
||||
const connectionRef = useRef<SseConnection | null>(null);
|
||||
const retryTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const stoppedRef = useRef(false);
|
||||
|
||||
const clearRetryTimer = useCallback(() => {
|
||||
if (!retryTimerRef.current) return;
|
||||
clearTimeout(retryTimerRef.current);
|
||||
retryTimerRef.current = null;
|
||||
}, []);
|
||||
|
||||
const closeConnection = useCallback(() => {
|
||||
connectionRef.current?.close();
|
||||
connectionRef.current = null;
|
||||
}, []);
|
||||
|
||||
const stop = useCallback(() => {
|
||||
stoppedRef.current = true;
|
||||
clearRetryTimer();
|
||||
closeConnection();
|
||||
}, [clearRetryTimer, closeConnection]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!enabled) return;
|
||||
|
||||
stoppedRef.current = false;
|
||||
|
||||
const scheduleReconnect = (notify = true) => {
|
||||
if (stoppedRef.current || retryTimerRef.current) return;
|
||||
|
||||
closeConnection();
|
||||
if (notify) onConnectionError?.();
|
||||
retryTimerRef.current = setTimeout(() => {
|
||||
retryTimerRef.current = null;
|
||||
connect();
|
||||
}, retryDelayMs);
|
||||
};
|
||||
|
||||
const connect = () => {
|
||||
if (stoppedRef.current) return;
|
||||
|
||||
getToken()
|
||||
.then(({ sse_token }) => {
|
||||
if (stoppedRef.current) return;
|
||||
|
||||
const wrappedEvents = Object.fromEntries(
|
||||
Object.entries(events).map(([eventName, handler]) => [
|
||||
eventName,
|
||||
(event: unknown, messageEvent: MessageEvent) => {
|
||||
(
|
||||
handler as (
|
||||
event: unknown,
|
||||
messageEvent: MessageEvent,
|
||||
) => void
|
||||
)(event, messageEvent);
|
||||
|
||||
if (
|
||||
shouldStop?.(
|
||||
eventName as keyof TEvents,
|
||||
event as TEvents[keyof TEvents],
|
||||
)
|
||||
) {
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (reconnectOnEvents.includes(eventName as keyof TEvents)) {
|
||||
scheduleReconnect(false);
|
||||
}
|
||||
},
|
||||
]),
|
||||
) as SseEventMap<TEvents>;
|
||||
|
||||
const connection = sseService.connect(
|
||||
getPath(sse_token),
|
||||
wrappedEvents,
|
||||
);
|
||||
|
||||
connection.source.onerror = () => scheduleReconnect();
|
||||
connectionRef.current = connection;
|
||||
})
|
||||
.catch(() => scheduleReconnect());
|
||||
};
|
||||
|
||||
connect();
|
||||
|
||||
return stop;
|
||||
}, [
|
||||
closeConnection,
|
||||
enabled,
|
||||
events,
|
||||
getPath,
|
||||
getToken,
|
||||
onConnectionError,
|
||||
reconnectOnEvents,
|
||||
retryDelayMs,
|
||||
shouldStop,
|
||||
stop,
|
||||
]);
|
||||
|
||||
return { stop };
|
||||
}
|
||||
Reference in New Issue
Block a user