Initial import

This commit is contained in:
how2ice
2026-04-21 03:33:23 +09:00
commit 9e4b70f1f1
495 changed files with 94680 additions and 0 deletions

View File

@@ -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,
};
}