676 lines
20 KiB
TypeScript
Executable File
676 lines
20 KiB
TypeScript
Executable File
import type { Dispatch, SetStateAction } from 'react';
|
|
import { useEffect, useState } from 'react';
|
|
import {
|
|
CHAT_CONNECTION,
|
|
diagnoseConnectionFailure,
|
|
getLastReceivedChatEventId,
|
|
handleChatServerEvent,
|
|
persistLastReceivedChatEventId,
|
|
resolveChatWebSocketUrl,
|
|
} from './chatUtils';
|
|
import { hasRegisteredAccessTokenAccess } from '../tokenAccess';
|
|
import type {
|
|
ChatActivityEvent,
|
|
ChatJobEvent,
|
|
ChatMessage,
|
|
ChatRuntimeJobDetail,
|
|
ChatRuntimeSnapshot,
|
|
ChatViewContext,
|
|
} from './types';
|
|
|
|
const DISCONNECT_UI_DELAY_MS = 1500;
|
|
const PRESENCE_PING_INTERVAL_MS = 20_000;
|
|
|
|
type ConnectionState = 'connecting' | 'connected' | 'disconnected';
|
|
|
|
type UseChatConnectionOptions = {
|
|
sessionId: string;
|
|
currentContext: ChatViewContext;
|
|
setMessages: Dispatch<SetStateAction<ChatMessage[]>>;
|
|
onMessageEvent?: (message: ChatMessage, sessionId: string) => void;
|
|
onJobEvent?: (event: ChatJobEvent, sessionId: string) => void;
|
|
onRuntimeEvent?: (snapshot: ChatRuntimeSnapshot) => void;
|
|
onRuntimeDetailEvent?: (detail: ChatRuntimeJobDetail) => void;
|
|
onActivityEvent?: (event: ChatActivityEvent) => void;
|
|
};
|
|
|
|
type SharedChatConnectionState = {
|
|
connectionState: ConnectionState;
|
|
connectionErrorDetail: string;
|
|
runtimeSnapshot: ChatRuntimeSnapshot | null;
|
|
};
|
|
|
|
type SharedChatConnection = SharedChatConnectionState & {
|
|
socketRef: { current: WebSocket | null };
|
|
reconnectTimerId: number | null;
|
|
disconnectUiTimerId: number | null;
|
|
connectTimeoutId: number | null;
|
|
sessionId: string;
|
|
currentContext: ChatViewContext | null;
|
|
setMessages: Dispatch<SetStateAction<ChatMessage[]>> | null;
|
|
onMessageEvent?: ((message: ChatMessage, sessionId: string) => void) | undefined;
|
|
onJobEvent?: ((event: ChatJobEvent, sessionId: string) => void) | undefined;
|
|
onRuntimeEvent?: ((snapshot: ChatRuntimeSnapshot) => void) | undefined;
|
|
onRuntimeDetailEvent?: ((detail: ChatRuntimeJobDetail) => void) | undefined;
|
|
onActivityEvent?: ((event: ChatActivityEvent) => void) | undefined;
|
|
lastEventId: number;
|
|
websocketUrl: string;
|
|
subscribers: Set<() => void>;
|
|
pingSubscriberCount: number;
|
|
consumerCount: number;
|
|
pingIntervalId: number | null;
|
|
visibilityHandlerInstalled: boolean;
|
|
pageShowHandlerInstalled: boolean;
|
|
focusHandlerInstalled: boolean;
|
|
onlineHandlerInstalled: boolean;
|
|
hasConnectedOnce: boolean;
|
|
suppressDisconnectNotification: boolean;
|
|
lastBackgroundAt: number | null;
|
|
};
|
|
|
|
const sharedChatConnection: SharedChatConnection = {
|
|
connectionState: 'connecting',
|
|
connectionErrorDetail: '',
|
|
runtimeSnapshot: null,
|
|
socketRef: { current: null },
|
|
reconnectTimerId: null,
|
|
disconnectUiTimerId: null,
|
|
connectTimeoutId: null,
|
|
sessionId: '',
|
|
currentContext: null,
|
|
setMessages: null,
|
|
onMessageEvent: undefined,
|
|
onJobEvent: undefined,
|
|
onRuntimeEvent: undefined,
|
|
onRuntimeDetailEvent: undefined,
|
|
onActivityEvent: undefined,
|
|
lastEventId: 0,
|
|
websocketUrl: '',
|
|
subscribers: new Set(),
|
|
pingSubscriberCount: 0,
|
|
consumerCount: 0,
|
|
pingIntervalId: null,
|
|
visibilityHandlerInstalled: false,
|
|
pageShowHandlerInstalled: false,
|
|
focusHandlerInstalled: false,
|
|
onlineHandlerInstalled: false,
|
|
hasConnectedOnce: false,
|
|
suppressDisconnectNotification: false,
|
|
lastBackgroundAt: null,
|
|
};
|
|
|
|
function emitSharedState() {
|
|
sharedChatConnection.subscribers.forEach((listener) => {
|
|
listener();
|
|
});
|
|
}
|
|
|
|
function getSnapshot(): SharedChatConnectionState {
|
|
return {
|
|
connectionState: sharedChatConnection.connectionState,
|
|
connectionErrorDetail: sharedChatConnection.connectionErrorDetail,
|
|
runtimeSnapshot: sharedChatConnection.runtimeSnapshot,
|
|
};
|
|
}
|
|
|
|
export function getChatConnectionSnapshot() {
|
|
return getSnapshot();
|
|
}
|
|
|
|
export function subscribeChatConnection(listener: () => void) {
|
|
sharedChatConnection.subscribers.add(listener);
|
|
|
|
return () => {
|
|
sharedChatConnection.subscribers.delete(listener);
|
|
};
|
|
}
|
|
|
|
export function getSharedChatRuntimeSnapshot() {
|
|
return sharedChatConnection.runtimeSnapshot;
|
|
}
|
|
|
|
export function setSharedChatRuntimeSnapshot(snapshot: ChatRuntimeSnapshot | null) {
|
|
if (sharedChatConnection.runtimeSnapshot === snapshot) {
|
|
return;
|
|
}
|
|
|
|
sharedChatConnection.runtimeSnapshot = snapshot;
|
|
emitSharedState();
|
|
}
|
|
|
|
function setSharedConnectionState(nextState: ConnectionState) {
|
|
if (sharedChatConnection.connectionState === nextState) {
|
|
return;
|
|
}
|
|
|
|
sharedChatConnection.connectionState = nextState;
|
|
emitSharedState();
|
|
}
|
|
|
|
function setSharedConnectionError(detail: string) {
|
|
if (sharedChatConnection.connectionErrorDetail === detail) {
|
|
return;
|
|
}
|
|
|
|
sharedChatConnection.connectionErrorDetail = detail;
|
|
emitSharedState();
|
|
}
|
|
|
|
function clearReconnectTimer() {
|
|
if (sharedChatConnection.reconnectTimerId !== null) {
|
|
window.clearTimeout(sharedChatConnection.reconnectTimerId);
|
|
sharedChatConnection.reconnectTimerId = null;
|
|
}
|
|
}
|
|
|
|
function clearDisconnectUiTimer() {
|
|
if (sharedChatConnection.disconnectUiTimerId !== null) {
|
|
window.clearTimeout(sharedChatConnection.disconnectUiTimerId);
|
|
sharedChatConnection.disconnectUiTimerId = null;
|
|
}
|
|
}
|
|
|
|
function clearConnectTimeout() {
|
|
if (sharedChatConnection.connectTimeoutId !== null) {
|
|
window.clearTimeout(sharedChatConnection.connectTimeoutId);
|
|
sharedChatConnection.connectTimeoutId = null;
|
|
}
|
|
}
|
|
|
|
function sendContextUpdate(context: ChatViewContext | null = sharedChatConnection.currentContext) {
|
|
const socket = sharedChatConnection.socketRef.current;
|
|
|
|
if (!socket || socket.readyState !== WebSocket.OPEN || !context) {
|
|
return;
|
|
}
|
|
|
|
const liveVisibilityState =
|
|
typeof document !== 'undefined' && document.visibilityState === 'hidden' ? 'hidden' : 'visible';
|
|
const livePageUrl = typeof window !== 'undefined' ? window.location.href : context.pageUrl;
|
|
|
|
socket.send(
|
|
JSON.stringify({
|
|
type: 'context:update',
|
|
payload: {
|
|
pageId: context.pageId,
|
|
pageTitle: context.pageTitle,
|
|
topMenu: context.topMenu,
|
|
focusedComponentId: context.focusedComponentId,
|
|
pageUrl: livePageUrl,
|
|
isStandaloneMode: context.isStandaloneMode,
|
|
pageVisibilityState: liveVisibilityState,
|
|
chatTypeId: context.chatTypeId,
|
|
chatTypeLabel: context.chatTypeLabel,
|
|
chatTypeDescription: context.chatTypeDescription,
|
|
chatTypeIsTemplate: context.chatTypeIsTemplate,
|
|
},
|
|
}),
|
|
);
|
|
}
|
|
|
|
function sendPresencePing() {
|
|
const socket = sharedChatConnection.socketRef.current;
|
|
|
|
if (!socket || socket.readyState !== WebSocket.OPEN) {
|
|
return;
|
|
}
|
|
|
|
socket.send(
|
|
JSON.stringify({
|
|
type: 'presence:ping',
|
|
payload: {
|
|
at: Date.now(),
|
|
},
|
|
}),
|
|
);
|
|
}
|
|
|
|
function ensureSharedSocket() {
|
|
const socket = sharedChatConnection.socketRef.current;
|
|
|
|
if (socket && (socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING)) {
|
|
return;
|
|
}
|
|
|
|
connectSharedSocket();
|
|
}
|
|
|
|
function sendEventReceived(eventId: number) {
|
|
const socket = sharedChatConnection.socketRef.current;
|
|
|
|
if (!socket || socket.readyState !== WebSocket.OPEN || !Number.isFinite(eventId) || eventId <= 0) {
|
|
return;
|
|
}
|
|
|
|
socket.send(
|
|
JSON.stringify({
|
|
type: 'event:received',
|
|
payload: {
|
|
eventId,
|
|
},
|
|
}),
|
|
);
|
|
}
|
|
|
|
function stopPresenceMonitoring() {
|
|
if (sharedChatConnection.pingIntervalId !== null) {
|
|
window.clearInterval(sharedChatConnection.pingIntervalId);
|
|
sharedChatConnection.pingIntervalId = null;
|
|
}
|
|
|
|
if (sharedChatConnection.visibilityHandlerInstalled) {
|
|
window.removeEventListener('visibilitychange', handleVisibilityChange);
|
|
sharedChatConnection.visibilityHandlerInstalled = false;
|
|
}
|
|
|
|
if (sharedChatConnection.pageShowHandlerInstalled) {
|
|
window.removeEventListener('pageshow', handlePageShow);
|
|
sharedChatConnection.pageShowHandlerInstalled = false;
|
|
}
|
|
|
|
if (sharedChatConnection.focusHandlerInstalled) {
|
|
window.removeEventListener('focus', handleWindowFocus);
|
|
sharedChatConnection.focusHandlerInstalled = false;
|
|
}
|
|
|
|
if (sharedChatConnection.onlineHandlerInstalled) {
|
|
window.removeEventListener('online', handleWindowOnline);
|
|
sharedChatConnection.onlineHandlerInstalled = false;
|
|
}
|
|
}
|
|
|
|
function handleVisibilityChange() {
|
|
if (typeof document !== 'undefined' && document.visibilityState === 'hidden') {
|
|
sharedChatConnection.lastBackgroundAt = Date.now();
|
|
sendContextUpdate(sharedChatConnection.currentContext);
|
|
sendPresencePing();
|
|
return;
|
|
}
|
|
|
|
ensureSharedSocket();
|
|
sendPresencePing();
|
|
sendContextUpdate(sharedChatConnection.currentContext);
|
|
}
|
|
|
|
function handlePageShow() {
|
|
ensureSharedSocket();
|
|
sendPresencePing();
|
|
sendContextUpdate(sharedChatConnection.currentContext);
|
|
}
|
|
|
|
function handleWindowFocus() {
|
|
ensureSharedSocket();
|
|
sendPresencePing();
|
|
}
|
|
|
|
function handleWindowOnline() {
|
|
ensureSharedSocket();
|
|
}
|
|
|
|
function startPresenceMonitoring() {
|
|
if (sharedChatConnection.pingSubscriberCount <= 0 || sharedChatConnection.connectionState !== 'connected') {
|
|
stopPresenceMonitoring();
|
|
return;
|
|
}
|
|
|
|
sharedChatConnection.lastBackgroundAt = null;
|
|
sendPresencePing();
|
|
|
|
if (sharedChatConnection.pingIntervalId === null) {
|
|
sharedChatConnection.pingIntervalId = window.setInterval(() => {
|
|
sendPresencePing();
|
|
}, PRESENCE_PING_INTERVAL_MS);
|
|
}
|
|
|
|
if (!sharedChatConnection.visibilityHandlerInstalled) {
|
|
window.addEventListener('visibilitychange', handleVisibilityChange);
|
|
sharedChatConnection.visibilityHandlerInstalled = true;
|
|
}
|
|
|
|
if (!sharedChatConnection.pageShowHandlerInstalled) {
|
|
window.addEventListener('pageshow', handlePageShow);
|
|
sharedChatConnection.pageShowHandlerInstalled = true;
|
|
}
|
|
|
|
if (!sharedChatConnection.focusHandlerInstalled) {
|
|
window.addEventListener('focus', handleWindowFocus);
|
|
sharedChatConnection.focusHandlerInstalled = true;
|
|
}
|
|
|
|
if (!sharedChatConnection.onlineHandlerInstalled) {
|
|
window.addEventListener('online', handleWindowOnline);
|
|
sharedChatConnection.onlineHandlerInstalled = true;
|
|
}
|
|
}
|
|
|
|
function scheduleReconnect() {
|
|
if (sharedChatConnection.reconnectTimerId !== null || !sharedChatConnection.sessionId) {
|
|
return;
|
|
}
|
|
|
|
sharedChatConnection.reconnectTimerId = window.setTimeout(() => {
|
|
sharedChatConnection.reconnectTimerId = null;
|
|
connectSharedSocket();
|
|
}, CHAT_CONNECTION.reconnectDelayMs);
|
|
}
|
|
|
|
function handleSharedDisconnect(message?: string, detail?: string) {
|
|
setSharedConnectionError(detail ?? '');
|
|
clearDisconnectUiTimer();
|
|
|
|
if (sharedChatConnection.connectionState !== 'connected') {
|
|
setSharedConnectionState('disconnected');
|
|
} else {
|
|
sharedChatConnection.disconnectUiTimerId = window.setTimeout(() => {
|
|
sharedChatConnection.disconnectUiTimerId = null;
|
|
|
|
if (sharedChatConnection.socketRef.current?.readyState === WebSocket.OPEN) {
|
|
return;
|
|
}
|
|
|
|
setSharedConnectionState('disconnected');
|
|
}, DISCONNECT_UI_DELAY_MS);
|
|
}
|
|
|
|
if (message) {
|
|
scheduleReconnect();
|
|
}
|
|
}
|
|
|
|
function disconnectSharedSocket() {
|
|
clearReconnectTimer();
|
|
clearConnectTimeout();
|
|
clearDisconnectUiTimer();
|
|
stopPresenceMonitoring();
|
|
|
|
const socket = sharedChatConnection.socketRef.current;
|
|
sharedChatConnection.suppressDisconnectNotification = true;
|
|
sharedChatConnection.socketRef.current = null;
|
|
socket?.close();
|
|
}
|
|
|
|
function releaseSharedConnectionConsumer() {
|
|
sharedChatConnection.consumerCount = Math.max(0, sharedChatConnection.consumerCount - 1);
|
|
|
|
if (sharedChatConnection.consumerCount > 0) {
|
|
return;
|
|
}
|
|
|
|
sharedChatConnection.currentContext = null;
|
|
sharedChatConnection.setMessages = null;
|
|
sharedChatConnection.onMessageEvent = undefined;
|
|
sharedChatConnection.onJobEvent = undefined;
|
|
sharedChatConnection.onRuntimeEvent = undefined;
|
|
sharedChatConnection.onRuntimeDetailEvent = undefined;
|
|
setSharedChatRuntimeSnapshot(null);
|
|
disconnectSharedSocket();
|
|
setSharedConnectionError('');
|
|
setSharedConnectionState('disconnected');
|
|
}
|
|
|
|
function connectSharedSocket() {
|
|
if (!sharedChatConnection.sessionId || !sharedChatConnection.setMessages) {
|
|
return;
|
|
}
|
|
|
|
if (!hasRegisteredAccessTokenAccess()) {
|
|
clearReconnectTimer();
|
|
clearConnectTimeout();
|
|
clearDisconnectUiTimer();
|
|
stopPresenceMonitoring();
|
|
setSharedConnectionError('등록된 접근 토큰이 없어 채팅 연결을 시작하지 않았습니다.');
|
|
setSharedConnectionState('disconnected');
|
|
return;
|
|
}
|
|
|
|
const currentSocket = sharedChatConnection.socketRef.current;
|
|
|
|
if (currentSocket && (currentSocket.readyState === WebSocket.OPEN || currentSocket.readyState === WebSocket.CONNECTING)) {
|
|
return;
|
|
}
|
|
|
|
clearReconnectTimer();
|
|
clearConnectTimeout();
|
|
clearDisconnectUiTimer();
|
|
|
|
if (sharedChatConnection.connectionState !== 'connected') {
|
|
setSharedConnectionState('connecting');
|
|
}
|
|
|
|
sharedChatConnection.websocketUrl = resolveChatWebSocketUrl(sharedChatConnection.sessionId, sharedChatConnection.lastEventId);
|
|
|
|
let socket: WebSocket;
|
|
|
|
try {
|
|
socket = new WebSocket(sharedChatConnection.websocketUrl);
|
|
} catch {
|
|
handleSharedDisconnect(
|
|
`워크서버 WebSocket 주소가 올바르지 않습니다. 대상: ${sharedChatConnection.websocketUrl || '/ws/chat'} 자동으로 다시 연결합니다.`,
|
|
'WebSocket 객체를 생성하지 못했습니다. 대상 주소 형식과 환경변수를 확인해 주세요.',
|
|
);
|
|
return;
|
|
}
|
|
|
|
sharedChatConnection.socketRef.current = socket;
|
|
sharedChatConnection.suppressDisconnectNotification = false;
|
|
let disconnectHandled = false;
|
|
|
|
const reportDisconnect = (message?: string, closeEvent?: CloseEvent) => {
|
|
if (disconnectHandled) {
|
|
return;
|
|
}
|
|
|
|
disconnectHandled = true;
|
|
|
|
const wasSuppressed = sharedChatConnection.suppressDisconnectNotification;
|
|
|
|
if (sharedChatConnection.socketRef.current === socket) {
|
|
sharedChatConnection.socketRef.current = null;
|
|
}
|
|
|
|
sharedChatConnection.suppressDisconnectNotification = false;
|
|
|
|
if (wasSuppressed) {
|
|
setSharedConnectionError('');
|
|
return;
|
|
}
|
|
|
|
if (closeEvent?.code === 1008) {
|
|
clearReconnectTimer();
|
|
setSharedConnectionError('등록된 접근 토큰이 없어 채팅 연결이 차단되었습니다.');
|
|
setSharedConnectionState('disconnected');
|
|
return;
|
|
}
|
|
|
|
if (closeEvent?.code === 1000 && !message) {
|
|
setSharedConnectionError('');
|
|
return;
|
|
}
|
|
|
|
void diagnoseConnectionFailure(sharedChatConnection.websocketUrl, closeEvent).then((detail) => {
|
|
handleSharedDisconnect(message, detail);
|
|
});
|
|
};
|
|
|
|
sharedChatConnection.connectTimeoutId = window.setTimeout(() => {
|
|
if (sharedChatConnection.socketRef.current !== socket || socket.readyState === WebSocket.OPEN) {
|
|
return;
|
|
}
|
|
|
|
sharedChatConnection.socketRef.current = null;
|
|
socket.close();
|
|
reportDisconnect(
|
|
`워크서버 연결 시간이 초과되었습니다. 대상: ${sharedChatConnection.websocketUrl || '/ws/chat'} 자동으로 다시 연결합니다.`,
|
|
);
|
|
}, CHAT_CONNECTION.connectTimeoutMs);
|
|
|
|
socket.addEventListener('open', () => {
|
|
clearConnectTimeout();
|
|
clearDisconnectUiTimer();
|
|
sharedChatConnection.hasConnectedOnce = true;
|
|
sharedChatConnection.suppressDisconnectNotification = false;
|
|
setSharedConnectionState('connected');
|
|
setSharedConnectionError('');
|
|
sendContextUpdate(sharedChatConnection.currentContext);
|
|
startPresenceMonitoring();
|
|
});
|
|
|
|
socket.addEventListener('message', (event) => {
|
|
const setMessages = sharedChatConnection.setMessages;
|
|
|
|
if (!setMessages) {
|
|
return;
|
|
}
|
|
|
|
void handleChatServerEvent({
|
|
eventData: String(event.data),
|
|
currentPageUrl: sharedChatConnection.currentContext?.pageUrl ?? '',
|
|
expectedSessionId: sharedChatConnection.sessionId,
|
|
setMessages,
|
|
onMessageEvent: sharedChatConnection.onMessageEvent,
|
|
onJobEvent: sharedChatConnection.onJobEvent,
|
|
onRuntimeEvent: sharedChatConnection.onRuntimeEvent,
|
|
onRuntimeDetailEvent: sharedChatConnection.onRuntimeDetailEvent,
|
|
onActivityEvent: sharedChatConnection.onActivityEvent,
|
|
onEventReceived: (eventId) => {
|
|
sharedChatConnection.lastEventId = eventId;
|
|
persistLastReceivedChatEventId(sharedChatConnection.sessionId, eventId);
|
|
sendEventReceived(eventId);
|
|
},
|
|
});
|
|
|
|
try {
|
|
const parsedEvent = JSON.parse(String(event.data)) as ChatRuntimeEventEnvelope | null;
|
|
|
|
if (parsedEvent?.type === 'chat:runtime') {
|
|
setSharedChatRuntimeSnapshot(parsedEvent.payload);
|
|
}
|
|
} catch {
|
|
// ignore malformed payloads here; detailed parsing is already handled downstream
|
|
}
|
|
});
|
|
|
|
socket.addEventListener('close', (event) => {
|
|
clearConnectTimeout();
|
|
stopPresenceMonitoring();
|
|
reportDisconnect(
|
|
event.code === 1000 ? undefined : '워크서버 연결이 끊어졌습니다. 자동으로 다시 연결합니다.',
|
|
event,
|
|
);
|
|
});
|
|
|
|
socket.addEventListener('error', () => {
|
|
clearConnectTimeout();
|
|
stopPresenceMonitoring();
|
|
reportDisconnect('워크서버 WebSocket 연결에 실패했습니다. 자동으로 다시 연결합니다.');
|
|
});
|
|
}
|
|
|
|
type ChatRuntimeEventEnvelope = {
|
|
type: 'chat:runtime';
|
|
payload: ChatRuntimeSnapshot;
|
|
};
|
|
|
|
function ensureSharedConnection(options: UseChatConnectionOptions) {
|
|
const sessionChanged = sharedChatConnection.sessionId !== options.sessionId;
|
|
|
|
sharedChatConnection.currentContext = options.currentContext;
|
|
sharedChatConnection.setMessages = options.setMessages;
|
|
sharedChatConnection.onMessageEvent = options.onMessageEvent;
|
|
sharedChatConnection.onJobEvent = options.onJobEvent;
|
|
sharedChatConnection.onRuntimeEvent = options.onRuntimeEvent;
|
|
sharedChatConnection.onRuntimeDetailEvent = options.onRuntimeDetailEvent;
|
|
sharedChatConnection.onActivityEvent = options.onActivityEvent;
|
|
|
|
if (sessionChanged) {
|
|
sharedChatConnection.sessionId = options.sessionId;
|
|
sharedChatConnection.lastEventId = getLastReceivedChatEventId(options.sessionId);
|
|
sharedChatConnection.hasConnectedOnce = false;
|
|
disconnectSharedSocket();
|
|
}
|
|
|
|
connectSharedSocket();
|
|
}
|
|
|
|
export function useChatConnection({
|
|
sessionId,
|
|
currentContext,
|
|
setMessages,
|
|
onMessageEvent,
|
|
onJobEvent,
|
|
onRuntimeEvent,
|
|
onRuntimeDetailEvent,
|
|
onActivityEvent,
|
|
}: UseChatConnectionOptions) {
|
|
const [snapshot, setSnapshot] = useState<SharedChatConnectionState>(() => getSnapshot());
|
|
|
|
useEffect(() => {
|
|
sharedChatConnection.consumerCount += 1;
|
|
|
|
return () => {
|
|
releaseSharedConnectionConsumer();
|
|
};
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
const handleSnapshotChange = () => {
|
|
setSnapshot(getSnapshot());
|
|
};
|
|
|
|
const unsubscribe = subscribeChatConnection(handleSnapshotChange);
|
|
ensureSharedConnection({
|
|
sessionId,
|
|
currentContext,
|
|
setMessages,
|
|
onMessageEvent,
|
|
onJobEvent,
|
|
onRuntimeEvent,
|
|
onRuntimeDetailEvent,
|
|
onActivityEvent,
|
|
});
|
|
handleSnapshotChange();
|
|
|
|
return () => {
|
|
unsubscribe();
|
|
};
|
|
}, [sessionId, setMessages]);
|
|
|
|
useEffect(() => {
|
|
sharedChatConnection.currentContext = currentContext;
|
|
sharedChatConnection.setMessages = setMessages;
|
|
sharedChatConnection.onMessageEvent = onMessageEvent;
|
|
sharedChatConnection.onJobEvent = onJobEvent;
|
|
sharedChatConnection.onRuntimeEvent = onRuntimeEvent;
|
|
sharedChatConnection.onRuntimeDetailEvent = onRuntimeDetailEvent;
|
|
sharedChatConnection.onActivityEvent = onActivityEvent;
|
|
sendContextUpdate(currentContext);
|
|
}, [
|
|
currentContext,
|
|
onMessageEvent,
|
|
onJobEvent,
|
|
onRuntimeEvent,
|
|
onRuntimeDetailEvent,
|
|
onActivityEvent,
|
|
setMessages,
|
|
]);
|
|
|
|
useEffect(() => {
|
|
sharedChatConnection.pingSubscriberCount += 1;
|
|
startPresenceMonitoring();
|
|
|
|
return () => {
|
|
sharedChatConnection.pingSubscriberCount = Math.max(0, sharedChatConnection.pingSubscriberCount - 1);
|
|
|
|
if (sharedChatConnection.pingSubscriberCount === 0) {
|
|
stopPresenceMonitoring();
|
|
}
|
|
};
|
|
}, []);
|
|
|
|
return {
|
|
connectionState: snapshot.connectionState,
|
|
connectionErrorDetail: snapshot.connectionErrorDetail,
|
|
socketRef: sharedChatConnection.socketRef,
|
|
};
|
|
}
|