buster/web/src/context/Chats/ChatProvider.tsx

194 lines
5.4 KiB
TypeScript
Raw Normal View History

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';
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);
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);
useEffect(() => {
if (chatId) subscribeToChat({ chatId });
}, [chatId]);
2025-01-28 05:20:26 +08:00
useUnmount(() => {
if (chatId) unsubscribeFromChat({ chatId });
2025-01-28 05:20:26 +08:00
});
return {
chat
2025-01-28 05:20:26 +08:00
};
};