From a262ea67a2915f2511681e8494c09fe46aab4b05 Mon Sep 17 00:00:00 2001 From: Renjie Li <renjie_li@outlook.com> Date: Tue, 27 May 2025 15:23:46 +0800 Subject: [PATCH] add console context --- client/modules/IDE/components/Console.jsx | 9 +- client/modules/IDE/context/ConsoleContext.jsx | 29 +++ .../IDE/hooks/useHandleMessageEvent.js | 5 +- client/modules/IDE/pages/IDEView.jsx | 212 +++++++++--------- 4 files changed, 143 insertions(+), 112 deletions(-) create mode 100644 client/modules/IDE/context/ConsoleContext.jsx diff --git a/client/modules/IDE/components/Console.jsx b/client/modules/IDE/components/Console.jsx index 12d5c4988b..454b503998 100644 --- a/client/modules/IDE/components/Console.jsx +++ b/client/modules/IDE/components/Console.jsx @@ -1,24 +1,21 @@ import React, { useRef, useEffect, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; - import { useSelector, useDispatch } from 'react-redux'; import classNames from 'classnames'; import { Console as ConsoleFeed } from 'console-feed'; import ConsoleInput from './ConsoleInput'; - import UpArrowIcon from '../../../images/up-arrow.svg'; import DownArrowIcon from '../../../images/down-arrow.svg'; - import * as IDEActions from '../../IDE/actions/ide'; -import * as ConsoleActions from '../../IDE/actions/console'; import { useDidUpdate } from '../hooks/custom-hooks'; import useHandleMessageEvent from '../hooks/useHandleMessageEvent'; import { listen } from '../../../utils/dispatcher'; import getConsoleFeedStyle from '../utils/consoleStyles'; +import { useConsole } from '../context/ConsoleContext'; const Console = () => { const { t } = useTranslation(); - const consoleEvents = useSelector((state) => state.console); + const { consoleEvents, clearConsole: clearConsoleContext } = useConsole(); const isExpanded = useSelector((state) => state.ide.consoleIsExpanded); const isPlaying = useSelector((state) => state.ide.isPlaying); const { theme, fontSize } = useSelector((state) => state.preferences); @@ -44,7 +41,7 @@ const Console = () => { }; }); - const handleClearConsole = () => dispatch(ConsoleActions.clearConsole()); + const handleClearConsole = () => clearConsoleContext(); const handleCollapseConsole = () => dispatch(IDEActions.collapseConsole()); const handleExpandConsole = () => dispatch(IDEActions.expandConsole()); diff --git a/client/modules/IDE/context/ConsoleContext.jsx b/client/modules/IDE/context/ConsoleContext.jsx new file mode 100644 index 0000000000..f1be4f31da --- /dev/null +++ b/client/modules/IDE/context/ConsoleContext.jsx @@ -0,0 +1,29 @@ +import React, { createContext, useContext, useState, useCallback } from 'react'; + +const ConsoleContext = createContext(null); + +export function ConsoleProvider({ children }) { + const [consoleEvents, setConsoleEvents] = useState([]); + + const dispatchConsoleEvent = useCallback((messages) => { + setConsoleEvents(prev => [...prev, ...messages]); + }, []); + + const clearConsole = useCallback(() => { + setConsoleEvents([]); + }, []); + + return ( + <ConsoleContext.Provider value={{ consoleEvents, dispatchConsoleEvent, clearConsole }}> + {children} + </ConsoleContext.Provider> + ); +} + +export function useConsole() { + const context = useContext(ConsoleContext); + if (!context) { + throw new Error('useConsole must be used within a ConsoleProvider'); + } + return context; +} \ No newline at end of file diff --git a/client/modules/IDE/hooks/useHandleMessageEvent.js b/client/modules/IDE/hooks/useHandleMessageEvent.js index 5603c1d697..7e9086a53d 100644 --- a/client/modules/IDE/hooks/useHandleMessageEvent.js +++ b/client/modules/IDE/hooks/useHandleMessageEvent.js @@ -1,10 +1,11 @@ import { useDispatch } from 'react-redux'; import { Decode } from 'console-feed'; -import { dispatchConsoleEvent } from '../actions/console'; import { stopSketch, expandConsole } from '../actions/ide'; +import { useConsole } from '../context/ConsoleContext'; export default function useHandleMessageEvent() { const dispatch = useDispatch(); + const { dispatchConsoleEvent } = useConsole(); const safeStringify = ( obj, @@ -62,7 +63,7 @@ export default function useHandleMessageEvent() { return; } - dispatch(dispatchConsoleEvent(decodedMessages)); + dispatchConsoleEvent(decodedMessages); }; return handleMessageEvent; diff --git a/client/modules/IDE/pages/IDEView.jsx b/client/modules/IDE/pages/IDEView.jsx index a9cc72f169..d569ec2e96 100644 --- a/client/modules/IDE/pages/IDEView.jsx +++ b/client/modules/IDE/pages/IDEView.jsx @@ -10,6 +10,7 @@ import PreviewFrame from '../components/PreviewFrame'; import Console from '../components/Console'; import Toast from '../components/Toast'; import { updateFileContent } from '../actions/files'; +import { ConsoleProvider } from '../context/ConsoleContext'; import { autosaveProject, @@ -167,127 +168,130 @@ const IDEView = () => { : consoleCollapsedSize; return ( - <RootPage> - <Helmet> - <title>{getTitle(project)}</title> - </Helmet> - <IDEKeyHandlers getContent={() => cmRef.current?.getContent()} /> - <WarnIfUnsavedChanges /> - <Toast /> - <P5VersionProvider> - <CmControllerContext.Provider value={cmRef}> - <Header syncFileContent={syncFileContent} /> - </CmControllerContext.Provider> - {isMobile ? ( - <> - <FloatingActionButton - syncFileContent={syncFileContent} - offsetBottom={ide.isPlaying ? currentConsoleSize : 0} - /> - <PreviewWrapper show={ide.isPlaying}> - <SplitPane - style={{ position: 'static' }} - split="horizontal" - primary="second" - size={currentConsoleSize} - minSize={consoleCollapsedSize} - onChange={(size) => { - setConsoleSize(size); - setIsOverlayVisible(true); - }} - onDragFinished={() => { - setIsOverlayVisible(false); - }} - allowResize={ide.consoleIsExpanded} - className="editor-preview-subpanel" - > - <PreviewFrame - fullView - hide={!ide.isPlaying} - cmController={cmRef.current} - isOverlayVisible={isOverlayVisible} - /> - <Console /> - </SplitPane> - </PreviewWrapper> - <EditorSidebarWrapper show={!ide.isPlaying}> - <Sidebar /> - <Editor - provideController={(ctl) => { - cmRef.current = ctl; - }} + <ConsoleProvider> + <RootPage> + <Helmet> + <title>{getTitle(project)}</title> + </Helmet> + <IDEKeyHandlers getContent={() => cmRef.current?.getContent()} /> + <WarnIfUnsavedChanges /> + <Toast /> + <P5VersionProvider> + <CmControllerContext.Provider value={cmRef}> + <Header syncFileContent={syncFileContent} /> + </CmControllerContext.Provider> + {isMobile ? ( + <> + <FloatingActionButton + syncFileContent={syncFileContent} + offsetBottom={ide.isPlaying ? currentConsoleSize : 0} /> - </EditorSidebarWrapper> - </> - ) : ( - <main className="editor-preview-container"> - <SplitPane - split="vertical" - size={ide.sidebarIsExpanded ? sidebarSize : 20} - onChange={(size) => { - setSidebarSize(size); - }} - allowResize={ide.sidebarIsExpanded} - minSize={150} - > - <Sidebar /> - <SplitPane - split="vertical" - maxSize={MaxSize * 0.965} - defaultSize="50%" - onChange={() => { - setIsOverlayVisible(true); - }} - onDragFinished={() => { - setIsOverlayVisible(false); - }} - resizerStyle={{ - borderLeftWidth: '2px', - borderRightWidth: '2px', - width: '2px', - margin: '0px 0px' - }} - > + <PreviewWrapper show={ide.isPlaying}> <SplitPane + style={{ position: 'static' }} split="horizontal" primary="second" size={currentConsoleSize} minSize={consoleCollapsedSize} onChange={(size) => { setConsoleSize(size); + setIsOverlayVisible(true); + }} + onDragFinished={() => { + setIsOverlayVisible(false); }} allowResize={ide.consoleIsExpanded} className="editor-preview-subpanel" > - <Editor - provideController={(ctl) => { - cmRef.current = ctl; - }} + <PreviewFrame + fullView + hide={!ide.isPlaying} + cmController={cmRef.current} + isOverlayVisible={isOverlayVisible} /> <Console /> </SplitPane> - <section className="preview-frame-holder"> - <header className="preview-frame__header"> - <h2 className="preview-frame__title"> - {t('Toolbar.Preview')} - </h2> - </header> - <div className="preview-frame__content"> - <PreviewFrame - cmController={cmRef.current} - isOverlayVisible={isOverlayVisible} + </PreviewWrapper> + <EditorSidebarWrapper show={!ide.isPlaying}> + <Sidebar /> + <Editor + provideController={(ctl) => { + cmRef.current = ctl; + }} + /> + </EditorSidebarWrapper> + </> + ) : ( + <main className="editor-preview-container"> + <SplitPane + split="vertical" + size={ide.sidebarIsExpanded ? sidebarSize : 20} + onChange={(size) => { + setSidebarSize(size); + }} + allowResize={ide.sidebarIsExpanded} + minSize={150} + > + <Sidebar /> + <SplitPane + split="vertical" + maxSize={MaxSize * 0.965} + defaultSize="50%" + onChange={() => { + setIsOverlayVisible(true); + }} + onDragFinished={() => { + setIsOverlayVisible(false); + }} + resizerStyle={{ + borderLeftWidth: '2px', + borderRightWidth: '2px', + width: '2px', + margin: '0px 0px' + }} + > + <SplitPane + split="horizontal" + primary="second" + size={currentConsoleSize} + minSize={consoleCollapsedSize} + onChange={(size) => { + setConsoleSize(size); + }} + allowResize={ide.consoleIsExpanded} + className="editor-preview-subpanel" + > + <Editor + provideController={(ctl) => { + cmRef.current = ctl; + }} /> - </div> - </section> + <Console /> + </SplitPane> + <section className="preview-frame-holder"> + <header className="preview-frame__header"> + <h2 className="preview-frame__title"> + {t('Toolbar.Preview')} + </h2> + </header> + <div className="preview-frame__content"> + <PreviewFrame + cmController={cmRef.current} + isOverlayVisible={isOverlayVisible} + /> + </div> + </section> + </SplitPane> </SplitPane> - </SplitPane> - </main> - )} - <CmControllerContext.Provider value={cmRef}> - <IDEOverlays /> - </CmControllerContext.Provider> - </P5VersionProvider> - </RootPage> + </main> + )} + <CmControllerContext.Provider value={cmRef}> + <IDEOverlays /> + </CmControllerContext.Provider> + </P5VersionProvider> + <Console /> + </RootPage> + </ConsoleProvider> ); };