From 9924952d96c824bc61a5ff8c5cfe78ec71df361d Mon Sep 17 00:00:00 2001 From: JinmingYang <2214962083@qq.com> Date: Fri, 30 Jun 2023 13:15:33 +0800 Subject: [PATCH] feat(gpt-runner-web): add use event emitter hook --- .../src/hooks/use-event-emitter.hook.ts | 52 +++++++++++++++++++ .../chat/components/chat-panel/index.tsx | 3 +- .../chat/components/chat-sidebar/index.tsx | 8 +-- .../pages/chat/components/file-tree/index.tsx | 8 +-- 4 files changed, 58 insertions(+), 13 deletions(-) create mode 100644 packages/gpt-runner-web/client/src/hooks/use-event-emitter.hook.ts diff --git a/packages/gpt-runner-web/client/src/hooks/use-event-emitter.hook.ts b/packages/gpt-runner-web/client/src/hooks/use-event-emitter.hook.ts new file mode 100644 index 0000000..b06f3e8 --- /dev/null +++ b/packages/gpt-runner-web/client/src/hooks/use-event-emitter.hook.ts @@ -0,0 +1,52 @@ +import { useEffect, useRef } from 'react' +import { emitter } from '../helpers/emitter' + +export interface UseEventEmitterReturns { + emit: typeof emitter.emit + on: typeof emitter.on + once: typeof emitter.once + off: typeof emitter.off +} + +export function useEventEmitter(): UseEventEmitterReturns { + const listenersRef = useRef void>>({}) + + const on = ( + eventName: string, + listener: (...args: any[]) => void, + ) => { + listenersRef.current[eventName] = listener + emitter.on(eventName as any, listener) + } + + const once = ( + eventName: string, + listener: (...args: any[]) => void, + ) => { + listenersRef.current[eventName] = listener + emitter.once(eventName as any, listener) + } + + const off = ( + eventName: string, + listener: (...args: any[]) => void, + ) => { + delete listenersRef.current[eventName] + emitter.off(eventName as any, listener) + } + + const emit = (eventName: string, ...args: any[]) => { + emitter.emit(eventName as any, ...args) + } + + useEffect(() => { + return () => { + Object.entries(listenersRef.current).forEach(([eventName, listener]) => { + if (listener) + emitter.off(eventName as any, listener) + }) + } + }, []) + + return { on, once, off, emit } as UseEventEmitterReturns +} diff --git a/packages/gpt-runner-web/client/src/pages/chat/components/chat-panel/index.tsx b/packages/gpt-runner-web/client/src/pages/chat/components/chat-panel/index.tsx index 49cdbba..e5255fc 100644 --- a/packages/gpt-runner-web/client/src/pages/chat/components/chat-panel/index.tsx +++ b/packages/gpt-runner-web/client/src/pages/chat/components/chat-panel/index.tsx @@ -13,7 +13,6 @@ import type { MessageItemProps } from '../../../../components/chat-message-item' import { ErrorView } from '../../../../components/error-view' import { useGlobalStore } from '../../../../store/zustand/global' import type { GptFileTreeItem } from '../../../../store/zustand/global/sidebar-tree.slice' -import { emitter } from '../../../../helpers/emitter' import { getGlobalConfig } from '../../../../helpers/global-config' import { PopoverMenu } from '../../../../components/popover-menu' import { useKeyboard } from '../../../../hooks/use-keyboard.hook' @@ -22,6 +21,7 @@ import { useElementSizeRealTime } from '../../../../hooks/use-element-size-real- import { useTempStore } from '../../../../store/zustand/temp' import type { MessageCodeBlockTheme } from '../../../../components/chat-message-code-block' import { isDarkTheme } from '../../../../styles/themes' +import { useEventEmitter } from '../../../../hooks/use-event-emitter.hook' import { ChatPanelPopoverTreeWrapper, ChatPanelWrapper } from './chat-panel.styles' import { createRemarkOpenEditorPlugin } from './remark-plugin' @@ -61,6 +61,7 @@ export const ChatPanel: FC = memo((props) => { const [gptFileTreeItem, setGptFileTreeItem] = useState() const [chatPanelRef, { width: chatPanelWidth }] = useElementSizeRealTime() const { filesRelativePaths } = useTempStore() + const emitter = useEventEmitter() const filesPathsAllPartsInfo = useMemo(() => { // not good, but fast diff --git a/packages/gpt-runner-web/client/src/pages/chat/components/chat-sidebar/index.tsx b/packages/gpt-runner-web/client/src/pages/chat/components/chat-sidebar/index.tsx index b3d22ca..d9c40f2 100644 --- a/packages/gpt-runner-web/client/src/pages/chat/components/chat-sidebar/index.tsx +++ b/packages/gpt-runner-web/client/src/pages/chat/components/chat-sidebar/index.tsx @@ -12,7 +12,7 @@ import { IconButton } from '../../../../components/icon-button' import { ErrorView } from '../../../../components/error-view' import { useGlobalStore } from '../../../../store/zustand/global' import { useChatInstance } from '../../../../hooks/use-chat-instance.hook' -import { emitter } from '../../../../helpers/emitter' +import { useEventEmitter } from '../../../../hooks/use-event-emitter.hook' export interface ChatSidebarProps { rootPath: string @@ -36,6 +36,7 @@ export const ChatSidebar: FC = memo((props) => { } = useGlobalStore() const [isLoading, setIsLoading] = useState(false) + const emitter = useEventEmitter() const { removeChatInstance } = useChatInstance({ chatId, @@ -61,11 +62,6 @@ export const ChatSidebar: FC = memo((props) => { emitter.on(ClientEventName.RefreshTree, refreshSidebarTree) emitter.on(ClientEventName.RefreshChatTree, refreshSidebarTree) - - return () => { - emitter.off(ClientEventName.RefreshTree, refreshSidebarTree) - emitter.off(ClientEventName.RefreshChatTree, refreshSidebarTree) - } }, [rootPath]) const handleCreateChat = useCallback((gptFileId: string) => { diff --git a/packages/gpt-runner-web/client/src/pages/chat/components/file-tree/index.tsx b/packages/gpt-runner-web/client/src/pages/chat/components/file-tree/index.tsx index 2bc2421..c4e60c6 100644 --- a/packages/gpt-runner-web/client/src/pages/chat/components/file-tree/index.tsx +++ b/packages/gpt-runner-web/client/src/pages/chat/components/file-tree/index.tsx @@ -16,7 +16,7 @@ import { useGlobalStore } from '../../../../store/zustand/global' import type { FileInfoSidebarTreeItem, FileSidebarTreeItem } from '../../../../store/zustand/global/file-tree.slice' import { PopoverMenu } from '../../../../components/popover-menu' import { useTempStore } from '../../../../store/zustand/temp' -import { emitter } from '../../../../helpers/emitter' +import { useEventEmitter } from '../../../../hooks/use-event-emitter.hook' import { FileTreeItemRightWrapper, FileTreeSidebarHighlight, FileTreeSidebarUnderSearchWrapper, FilterWrapper } from './file-tree.styles' export interface FileTreeProps { @@ -28,6 +28,7 @@ export const FileTree: FC = memo((props: FileTreeProps) => { const { rootPath, reverseTreeUi } = props const { t } = useTranslation() + const emitter = useEventEmitter() const [filesTree, _setFilesTree] = useState([]) const fullPathFileMapRef = useRef>({}) const { @@ -89,11 +90,6 @@ export const FileTree: FC = memo((props: FileTreeProps) => { emitter.on(ClientEventName.RefreshTree, refresh) emitter.on(ClientEventName.RefreshFileTree, refresh) - - return () => { - emitter.off(ClientEventName.RefreshTree, refresh) - emitter.off(ClientEventName.RefreshFileTree, refresh) - } }, [rootPath]) // sync checked state