2025-01-28 07:23:08 +08:00
|
|
|
import React, { useEffect, useRef, useTransition } from 'react';
|
2025-01-28 05:20:26 +08:00
|
|
|
import {
|
|
|
|
createContext,
|
|
|
|
ContextSelector,
|
|
|
|
useContextSelector
|
|
|
|
} from '@fluentui/react-context-selector';
|
|
|
|
import { useBusterWebSocket } from '../BusterWebSocket';
|
2025-01-31 07:56:08 +08:00
|
|
|
import type { BusterChat } from '@/api/buster_socket/chats';
|
2025-01-28 07:23:08 +08:00
|
|
|
import { useMemoizedFn, useUnmount } from 'ahooks';
|
2025-01-28 05:20:26 +08:00
|
|
|
import type { FileType } from '@/api/buster_socket/chats';
|
2025-01-30 04:58:27 +08:00
|
|
|
import {
|
|
|
|
createMockResponseMessageFile,
|
|
|
|
createMockResponseMessageText,
|
|
|
|
createMockResponseMessageThought,
|
|
|
|
MOCK_CHAT
|
|
|
|
} from './MOCK_CHAT';
|
2025-01-29 01:04:54 +08:00
|
|
|
import { IBusterChat } from './interfaces';
|
2025-01-30 04:08:33 +08:00
|
|
|
import { chatUpgrader } from './helpers';
|
2025-01-29 05:07:30 +08:00
|
|
|
import { useHotkeys } from 'react-hotkeys-hook';
|
2025-01-28 05:20:26 +08:00
|
|
|
|
|
|
|
export const useBusterChat = () => {
|
|
|
|
const busterSocket = useBusterWebSocket();
|
|
|
|
const [isPending, startTransition] = useTransition();
|
|
|
|
const chatsRef = useRef<Record<string, IBusterChat>>({});
|
|
|
|
|
|
|
|
// GETTERS
|
|
|
|
|
|
|
|
// SETTERS
|
|
|
|
|
|
|
|
// LISTENERS
|
|
|
|
|
2025-01-29 01:04:54 +08:00
|
|
|
const _onGetChat = useMemoizedFn((chat: BusterChat): IBusterChat => {
|
|
|
|
const upgradedChat = chatUpgrader(chat);
|
|
|
|
chatsRef.current[chat.id] = upgradedChat;
|
2025-01-28 05:20:26 +08:00
|
|
|
startTransition(() => {
|
|
|
|
//just used to trigger UI update
|
|
|
|
});
|
2025-01-29 01:04:54 +08:00
|
|
|
return upgradedChat;
|
2025-01-28 05:20:26 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
// EMITTERS
|
|
|
|
|
|
|
|
const unsubscribeFromChat = useMemoizedFn(({ chatId }: { chatId: string }) => {
|
|
|
|
return busterSocket.emit({
|
|
|
|
route: '/chats/unsubscribe',
|
|
|
|
payload: {
|
|
|
|
id: chatId
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
const subscribeToChat = useMemoizedFn(({ chatId }: { chatId: string }) => {
|
2025-01-29 01:04:54 +08:00
|
|
|
_onGetChat(MOCK_CHAT);
|
|
|
|
// return busterSocket.emitAndOnce({
|
|
|
|
// emitEvent: {
|
|
|
|
// route: '/chats/get',
|
|
|
|
// payload: { id: chatId }
|
|
|
|
// },
|
|
|
|
// responseEvent: {
|
|
|
|
// route: '/chats/get:getChat',
|
|
|
|
// callback: _onGetChat
|
|
|
|
// }
|
|
|
|
// });
|
2025-01-28 05:20:26 +08:00
|
|
|
});
|
|
|
|
|
2025-01-30 04:58:27 +08:00
|
|
|
useHotkeys('t', () => {
|
2025-01-29 05:07:30 +08:00
|
|
|
const chatId = Object.keys(chatsRef.current)[0];
|
|
|
|
if (chatId) {
|
|
|
|
const chat = chatsRef.current[chatId];
|
|
|
|
const mockMessage = createMockResponseMessageThought();
|
|
|
|
const newChat = { ...chat };
|
|
|
|
const firstMessage = {
|
|
|
|
...newChat.messages[0],
|
|
|
|
isCompletedStream: false,
|
|
|
|
response_messages: [...newChat.messages[0].response_messages, mockMessage]
|
|
|
|
};
|
|
|
|
newChat.messages = [firstMessage];
|
|
|
|
chatsRef.current[chatId] = newChat;
|
|
|
|
startTransition(() => {
|
|
|
|
//just used to trigger UI update
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2025-01-30 04:08:33 +08:00
|
|
|
useHotkeys('f', () => {
|
2025-01-30 04:58:27 +08:00
|
|
|
const chatId = Object.keys(chatsRef.current)[0];
|
|
|
|
if (chatId) {
|
|
|
|
const chat = chatsRef.current[chatId];
|
|
|
|
const mockMessage = createMockResponseMessageFile();
|
|
|
|
const newChat = { ...chat };
|
|
|
|
const firstMessage = {
|
|
|
|
...newChat.messages[0],
|
|
|
|
isCompletedStream: false,
|
|
|
|
response_messages: [...newChat.messages[0].response_messages, mockMessage]
|
|
|
|
};
|
|
|
|
newChat.messages = [firstMessage];
|
|
|
|
chatsRef.current[chatId] = newChat;
|
|
|
|
startTransition(() => {
|
|
|
|
//just used to trigger UI update
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
useHotkeys('r', () => {
|
|
|
|
const chatId = Object.keys(chatsRef.current)[0];
|
|
|
|
if (chatId) {
|
|
|
|
const chat = chatsRef.current[chatId];
|
|
|
|
const mockMessage = createMockResponseMessageText();
|
|
|
|
const newChat = { ...chat };
|
|
|
|
const firstMessage = {
|
|
|
|
...newChat.messages[0],
|
|
|
|
isCompletedStream: false,
|
|
|
|
response_messages: [...newChat.messages[0].response_messages, mockMessage]
|
|
|
|
};
|
|
|
|
newChat.messages = [firstMessage];
|
|
|
|
chatsRef.current[chatId] = newChat;
|
|
|
|
startTransition(() => {
|
|
|
|
//just used to trigger UI update
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
useHotkeys('h', () => {
|
2025-01-30 04:08:33 +08:00
|
|
|
const chatId = Object.keys(chatsRef.current)[0];
|
|
|
|
if (chatId) {
|
|
|
|
const chat = chatsRef.current[chatId];
|
|
|
|
|
|
|
|
const newChat = { ...chat };
|
|
|
|
const firstMessage = newChat.messages[0];
|
|
|
|
const updatedResponseMessages = firstMessage.response_messages.map((msg) => {
|
|
|
|
if (msg.type === 'thought') {
|
|
|
|
return {
|
|
|
|
...msg,
|
|
|
|
hidden: true
|
|
|
|
};
|
|
|
|
}
|
|
|
|
return msg;
|
|
|
|
});
|
|
|
|
|
|
|
|
const updatedMessage = {
|
|
|
|
...firstMessage,
|
|
|
|
response_messages: updatedResponseMessages
|
|
|
|
};
|
|
|
|
|
|
|
|
newChat.messages = [updatedMessage];
|
|
|
|
chatsRef.current[chatId] = newChat;
|
|
|
|
startTransition(() => {
|
|
|
|
// Trigger UI update
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2025-01-28 05:20:26 +08:00
|
|
|
return {
|
|
|
|
chats: chatsRef.current,
|
|
|
|
unsubscribeFromChat,
|
2025-01-31 07:29:06 +08:00
|
|
|
subscribeToChat
|
2025-01-28 05:20:26 +08:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
const BusterChat = createContext<ReturnType<typeof useBusterChat>>(
|
|
|
|
{} as ReturnType<typeof useBusterChat>
|
|
|
|
);
|
|
|
|
|
|
|
|
export const BusterChatProvider: React.FC<{
|
|
|
|
children: React.ReactNode;
|
|
|
|
}> = ({ children }) => {
|
|
|
|
const value = useBusterChat();
|
|
|
|
|
|
|
|
return <BusterChat.Provider value={value}>{children}</BusterChat.Provider>;
|
|
|
|
};
|
|
|
|
|
|
|
|
export const useBusterChatContextSelector = <T,>(
|
|
|
|
selector: ContextSelector<ReturnType<typeof useBusterChat>, T>
|
|
|
|
) => useContextSelector(BusterChat, selector);
|
|
|
|
|
2025-01-28 07:23:08 +08:00
|
|
|
export const useBusterChatIndividual = ({ chatId: chatIdProp }: { chatId?: string }) => {
|
|
|
|
const chatId = chatIdProp || '';
|
2025-01-28 05:20:26 +08:00
|
|
|
const chat = useBusterChatContextSelector((x) => x.chats[chatId]);
|
|
|
|
const subscribeToChat = useBusterChatContextSelector((x) => x.subscribeToChat);
|
|
|
|
const unsubscribeFromChat = useBusterChatContextSelector((x) => x.unsubscribeFromChat);
|
|
|
|
|
2025-01-28 07:23:08 +08:00
|
|
|
useEffect(() => {
|
|
|
|
if (chatId) subscribeToChat({ chatId });
|
|
|
|
}, [chatId]);
|
2025-01-28 05:20:26 +08:00
|
|
|
|
|
|
|
useUnmount(() => {
|
2025-01-28 07:23:08 +08:00
|
|
|
if (chatId) unsubscribeFromChat({ chatId });
|
2025-01-28 05:20:26 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
return {
|
2025-01-28 07:23:08 +08:00
|
|
|
chat
|
2025-01-28 05:20:26 +08:00
|
|
|
};
|
|
|
|
};
|