Initial import
This commit is contained in:
@@ -0,0 +1,370 @@
|
||||
import { useCallback } from 'react';
|
||||
import { removeChatRuntimeJob } from '../../mainChatPanel';
|
||||
import { chatConnectionGateway } from '../data/chatConnectionGateway';
|
||||
import { chatGateway } from '../data/chatGateway';
|
||||
import type {
|
||||
ChatComposerAttachment,
|
||||
ChatConversationRequest,
|
||||
ChatConversationSummary,
|
||||
ChatMessage,
|
||||
} from '../../mainChatPanel/types';
|
||||
|
||||
type PendingChatRequest = {
|
||||
sessionId: string;
|
||||
requestId: string;
|
||||
text: string;
|
||||
mode: 'queue' | 'direct';
|
||||
chatTypeId: string;
|
||||
chatTypeLabel: string;
|
||||
chatTypeDescription: string;
|
||||
chatTypeIsTemplate: boolean;
|
||||
retryCount: number;
|
||||
failed: boolean;
|
||||
};
|
||||
|
||||
type UseConversationRoomActionsControllerOptions = {
|
||||
activeSessionId: string;
|
||||
requestedSessionId: string;
|
||||
conversationItems: ChatConversationSummary[];
|
||||
activeConversation: ChatConversationSummary | null;
|
||||
editingConversationTitle: string;
|
||||
isMobileViewport: boolean;
|
||||
pendingRequestsRef: { current: PendingChatRequest[] };
|
||||
sessionMessageCacheRef: { current: Map<string, ChatMessage[]> };
|
||||
socketRef: { current: WebSocket | null };
|
||||
setConversationItems: React.Dispatch<React.SetStateAction<ChatConversationSummary[]>>;
|
||||
setMessages: React.Dispatch<React.SetStateAction<ChatMessage[]>>;
|
||||
setRequestItems: React.Dispatch<React.SetStateAction<ChatConversationRequest[]>>;
|
||||
setActiveSessionId: (value: string) => void;
|
||||
setDraft: (value: string) => void;
|
||||
setComposerAttachments: React.Dispatch<React.SetStateAction<ChatComposerAttachment[]>>;
|
||||
setCopiedMessageId: (value: number | null) => void;
|
||||
setActivePreviewId: (value: string | null) => void;
|
||||
setIsPreviewModalOpen: (value: boolean) => void;
|
||||
setActiveSystemStatus: (value: string | null) => void;
|
||||
setIsSystemStatusPending: (value: boolean) => void;
|
||||
setIsResourceStripOpen: (value: boolean) => void;
|
||||
setIsConversationPaneClosed: (value: boolean) => void;
|
||||
setIsMobileConversationView: (value: boolean) => void;
|
||||
setRenamingConversationSessionId: (value: string | null | ((current: string | null) => string | null)) => void;
|
||||
setEditingConversationTitle: (value: string) => void;
|
||||
setIsEditingConversationTitle: (value: boolean) => void;
|
||||
updatePendingMessageStatus: (requestId: string, status: 'retrying' | 'failed' | null, retryCount?: number) => void;
|
||||
sendChatRequest: (socket: WebSocket, request: PendingChatRequest) => void;
|
||||
createLocalMessage: (text: string) => ChatMessage;
|
||||
replaceChatSessionInUrl: (sessionId: string) => void;
|
||||
messageApi: {
|
||||
error: (content: string) => void;
|
||||
};
|
||||
};
|
||||
|
||||
export function useConversationRoomActionsController({
|
||||
activeSessionId,
|
||||
requestedSessionId,
|
||||
conversationItems,
|
||||
activeConversation,
|
||||
editingConversationTitle,
|
||||
isMobileViewport,
|
||||
pendingRequestsRef,
|
||||
sessionMessageCacheRef,
|
||||
socketRef,
|
||||
setConversationItems,
|
||||
setMessages,
|
||||
setRequestItems,
|
||||
setActiveSessionId,
|
||||
setDraft,
|
||||
setComposerAttachments,
|
||||
setCopiedMessageId,
|
||||
setActivePreviewId,
|
||||
setIsPreviewModalOpen,
|
||||
setActiveSystemStatus,
|
||||
setIsSystemStatusPending,
|
||||
setIsResourceStripOpen,
|
||||
setIsConversationPaneClosed,
|
||||
setIsMobileConversationView,
|
||||
setRenamingConversationSessionId,
|
||||
setEditingConversationTitle,
|
||||
setIsEditingConversationTitle,
|
||||
updatePendingMessageStatus,
|
||||
sendChatRequest,
|
||||
createLocalMessage,
|
||||
replaceChatSessionInUrl,
|
||||
messageApi,
|
||||
}: UseConversationRoomActionsControllerOptions) {
|
||||
const removeOptimisticRequestMessages = useCallback(
|
||||
(requestId: string) => {
|
||||
setMessages((previous) => previous.filter((message) => message.clientRequestId !== requestId));
|
||||
},
|
||||
[setMessages],
|
||||
);
|
||||
|
||||
const retryPendingRequest = useCallback(
|
||||
(requestId: string) => {
|
||||
const currentRequest = pendingRequestsRef.current.find(
|
||||
(request) => request.requestId === requestId && request.sessionId === activeSessionId,
|
||||
);
|
||||
|
||||
if (!currentRequest) {
|
||||
setMessages((previous) => [
|
||||
...previous.slice(-39),
|
||||
createLocalMessage('재전송할 요청 정보를 찾지 못했습니다. 같은 내용을 다시 보내 주세요.'),
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
const resetRequest: PendingChatRequest = {
|
||||
...currentRequest,
|
||||
retryCount: 0,
|
||||
failed: false,
|
||||
};
|
||||
|
||||
setActiveSystemStatus('전송 재시도 중...');
|
||||
setIsSystemStatusPending(true);
|
||||
|
||||
const socket = socketRef.current;
|
||||
|
||||
if (!socket || socket.readyState !== WebSocket.OPEN) {
|
||||
updatePendingMessageStatus(requestId, 'retrying', 0);
|
||||
pendingRequestsRef.current = [
|
||||
...pendingRequestsRef.current.filter((request) => request.requestId !== requestId),
|
||||
resetRequest,
|
||||
];
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
sendChatRequest(socket, resetRequest);
|
||||
updatePendingMessageStatus(requestId, null, 0);
|
||||
pendingRequestsRef.current = pendingRequestsRef.current.filter((request) => request.requestId !== requestId);
|
||||
} catch {
|
||||
updatePendingMessageStatus(requestId, 'retrying', 0);
|
||||
pendingRequestsRef.current = [
|
||||
...pendingRequestsRef.current.filter((request) => request.requestId !== requestId),
|
||||
resetRequest,
|
||||
];
|
||||
}
|
||||
},
|
||||
[
|
||||
activeSessionId,
|
||||
createLocalMessage,
|
||||
pendingRequestsRef,
|
||||
sendChatRequest,
|
||||
setActiveSystemStatus,
|
||||
setIsSystemStatusPending,
|
||||
setMessages,
|
||||
socketRef,
|
||||
updatePendingMessageStatus,
|
||||
],
|
||||
);
|
||||
|
||||
const cancelPendingRequest = useCallback(
|
||||
(requestId: string) => {
|
||||
const currentRequest = pendingRequestsRef.current.find(
|
||||
(request) => request.requestId === requestId && request.sessionId === activeSessionId,
|
||||
);
|
||||
|
||||
if (!currentRequest) {
|
||||
removeOptimisticRequestMessages(requestId);
|
||||
setActiveSystemStatus('미접수 요청을 화면에서 제거했습니다.');
|
||||
setIsSystemStatusPending(false);
|
||||
return;
|
||||
}
|
||||
|
||||
pendingRequestsRef.current = pendingRequestsRef.current.filter((request) => request.requestId !== requestId);
|
||||
removeOptimisticRequestMessages(requestId);
|
||||
setRequestItems((previous) =>
|
||||
previous.filter((item) => !(item.sessionId === activeSessionId && item.requestId === requestId)),
|
||||
);
|
||||
setActiveSystemStatus('실패한 요청을 취소했습니다.');
|
||||
setIsSystemStatusPending(false);
|
||||
},
|
||||
[
|
||||
activeSessionId,
|
||||
pendingRequestsRef,
|
||||
removeOptimisticRequestMessages,
|
||||
setActiveSystemStatus,
|
||||
setIsSystemStatusPending,
|
||||
setRequestItems,
|
||||
],
|
||||
);
|
||||
|
||||
const removeQueuedComposerRequest = useCallback(
|
||||
async (requestId: string) => {
|
||||
try {
|
||||
await removeChatRuntimeJob(requestId);
|
||||
setRequestItems((previous) =>
|
||||
previous.map((item) =>
|
||||
item.sessionId === activeSessionId && item.requestId === requestId
|
||||
? {
|
||||
...item,
|
||||
status: 'removed',
|
||||
statusMessage: '대기열에서 제거되었습니다.',
|
||||
terminalAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
}
|
||||
: item,
|
||||
),
|
||||
);
|
||||
setActiveSystemStatus('대기 요청을 삭제했습니다.');
|
||||
setIsSystemStatusPending(false);
|
||||
} catch (error) {
|
||||
setMessages((previous) => [
|
||||
...previous.slice(-39),
|
||||
createLocalMessage(error instanceof Error ? error.message : '대기 요청 제거 중 오류가 발생했습니다.'),
|
||||
]);
|
||||
}
|
||||
},
|
||||
[activeSessionId, createLocalMessage, setActiveSystemStatus, setIsSystemStatusPending, setMessages, setRequestItems],
|
||||
);
|
||||
|
||||
const deleteStoredRequest = useCallback(
|
||||
async (requestId: string) => {
|
||||
try {
|
||||
await chatGateway.deleteConversationRequest(activeSessionId, requestId);
|
||||
setMessages((previous) => previous.filter((message) => message.clientRequestId !== requestId));
|
||||
setRequestItems((previous) =>
|
||||
previous.filter((item) => !(item.sessionId === activeSessionId && item.requestId === requestId)),
|
||||
);
|
||||
setConversationItems((previous) =>
|
||||
previous.map((item) =>
|
||||
item.sessionId === activeSessionId
|
||||
? {
|
||||
...item,
|
||||
currentRequestId: item.currentRequestId === requestId ? null : item.currentRequestId,
|
||||
currentJobStatus: item.currentRequestId === requestId ? null : item.currentJobStatus,
|
||||
currentJobMessage: item.currentRequestId === requestId ? null : item.currentJobMessage,
|
||||
currentQueueSize: item.currentRequestId === requestId ? 0 : item.currentQueueSize,
|
||||
}
|
||||
: item,
|
||||
),
|
||||
);
|
||||
setActiveSystemStatus('요청을 삭제했습니다.');
|
||||
setIsSystemStatusPending(false);
|
||||
} catch (error) {
|
||||
setMessages((previous) => [
|
||||
...previous.slice(-39),
|
||||
createLocalMessage(error instanceof Error ? error.message : '요청 삭제 중 오류가 발생했습니다.'),
|
||||
]);
|
||||
}
|
||||
},
|
||||
[
|
||||
activeSessionId,
|
||||
createLocalMessage,
|
||||
setActiveSystemStatus,
|
||||
setConversationItems,
|
||||
setIsSystemStatusPending,
|
||||
setMessages,
|
||||
setRequestItems,
|
||||
],
|
||||
);
|
||||
|
||||
const handleRenameConversation = useCallback(async () => {
|
||||
if (!activeConversation) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sessionId = activeConversation.sessionId;
|
||||
const previousTitle = activeConversation.title;
|
||||
const trimmedTitle = editingConversationTitle.trim();
|
||||
|
||||
if (!trimmedTitle || trimmedTitle === previousTitle) {
|
||||
setIsEditingConversationTitle(false);
|
||||
setEditingConversationTitle(previousTitle);
|
||||
return;
|
||||
}
|
||||
|
||||
setRenamingConversationSessionId(sessionId);
|
||||
setConversationItems((previous) =>
|
||||
previous.map((entry) => (entry.sessionId === sessionId ? { ...entry, title: trimmedTitle } : entry)),
|
||||
);
|
||||
setEditingConversationTitle(trimmedTitle);
|
||||
setIsEditingConversationTitle(false);
|
||||
|
||||
try {
|
||||
const item = await chatGateway.renameConversation(sessionId, trimmedTitle);
|
||||
setConversationItems((previous) => previous.map((entry) => (entry.sessionId === item.sessionId ? item : entry)));
|
||||
setEditingConversationTitle(item.title);
|
||||
} catch (error) {
|
||||
setConversationItems((previous) =>
|
||||
previous.map((entry) => (entry.sessionId === sessionId ? { ...entry, title: previousTitle } : entry)),
|
||||
);
|
||||
setEditingConversationTitle(previousTitle);
|
||||
messageApi.error(error instanceof Error ? error.message : '채팅방 이름 변경에 실패했습니다.');
|
||||
} finally {
|
||||
setRenamingConversationSessionId((current) => (current === sessionId ? null : current));
|
||||
}
|
||||
}, [
|
||||
activeConversation,
|
||||
editingConversationTitle,
|
||||
messageApi,
|
||||
setConversationItems,
|
||||
setEditingConversationTitle,
|
||||
setIsEditingConversationTitle,
|
||||
setRenamingConversationSessionId,
|
||||
]);
|
||||
|
||||
const handleDeleteConversation = useCallback(
|
||||
async (sessionId: string) => {
|
||||
try {
|
||||
await chatGateway.deleteConversation(sessionId);
|
||||
const remaining = conversationItems.filter((entry) => entry.sessionId !== sessionId);
|
||||
sessionMessageCacheRef.current.delete(sessionId);
|
||||
setConversationItems(remaining);
|
||||
|
||||
if (sessionId === activeSessionId) {
|
||||
replaceChatSessionInUrl('');
|
||||
chatConnectionGateway.resetLastReceivedEventId('');
|
||||
setActiveSessionId('');
|
||||
setMessages([]);
|
||||
setRequestItems([]);
|
||||
setDraft('');
|
||||
setComposerAttachments([]);
|
||||
setCopiedMessageId(null);
|
||||
setActivePreviewId(null);
|
||||
setIsPreviewModalOpen(false);
|
||||
setActiveSystemStatus(null);
|
||||
setIsSystemStatusPending(false);
|
||||
setIsResourceStripOpen(false);
|
||||
setIsConversationPaneClosed(false);
|
||||
setIsMobileConversationView(!isMobileViewport);
|
||||
} else if (requestedSessionId === sessionId) {
|
||||
replaceChatSessionInUrl(activeSessionId);
|
||||
}
|
||||
} catch (error) {
|
||||
messageApi.error(error instanceof Error ? error.message : '대화방 삭제 중 오류가 발생했습니다.');
|
||||
}
|
||||
},
|
||||
[
|
||||
activeSessionId,
|
||||
conversationItems,
|
||||
isMobileViewport,
|
||||
messageApi,
|
||||
replaceChatSessionInUrl,
|
||||
requestedSessionId,
|
||||
sessionMessageCacheRef,
|
||||
setActivePreviewId,
|
||||
setActiveSessionId,
|
||||
setActiveSystemStatus,
|
||||
setComposerAttachments,
|
||||
setConversationItems,
|
||||
setCopiedMessageId,
|
||||
setDraft,
|
||||
setIsConversationPaneClosed,
|
||||
setIsMobileConversationView,
|
||||
setIsPreviewModalOpen,
|
||||
setIsResourceStripOpen,
|
||||
setIsSystemStatusPending,
|
||||
setMessages,
|
||||
setRequestItems,
|
||||
],
|
||||
);
|
||||
|
||||
return {
|
||||
cancelPendingRequest,
|
||||
deleteStoredRequest,
|
||||
handleDeleteConversation,
|
||||
handleRenameConversation,
|
||||
removeQueuedComposerRequest,
|
||||
retryPendingRequest,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user