From 760f16594e8bf25c25acea2bcbb9bac40b2c038c Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Wed, 2 Jul 2025 00:53:39 +0800 Subject: [PATCH 1/5] Create custom hook and fix memory leaks --- src/commons/utils/hooks/useIsVscode.tsx | 90 +++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 src/commons/utils/hooks/useIsVscode.tsx diff --git a/src/commons/utils/hooks/useIsVscode.tsx b/src/commons/utils/hooks/useIsVscode.tsx new file mode 100644 index 0000000000..71b70d5f4a --- /dev/null +++ b/src/commons/utils/hooks/useIsVscode.tsx @@ -0,0 +1,90 @@ +import { useEffect } from 'react'; +import { useDispatch } from 'react-redux'; +import SessionActions from 'src/commons/application/actions/SessionActions'; +import VscodeActions from 'src/commons/application/actions/VscodeActions'; +import WorkspaceActions from 'src/commons/workspace/WorkspaceActions'; +import Messages, { + MessageType, + MessageTypeNames, + sendToWebview +} from 'src/features/vscode/messages'; + +/** + * To handle messages from VS Code. + */ +export const useIsVscode = () => { + const dispatch = useDispatch(); + + useEffect(() => { + const originalConfirm = window.confirm; + + if (!window.confirm) { + // Polyfill confirm() to instead show as VS Code notification + // TODO: Pass text as a new Message to the webview + window.confirm = text => { + console.log(`Confirmation automatically accepted: ${text ?? 'No text provided'}`); + return true; + }; + } + + const message = Messages.ExtensionPing(window.origin); + sendToWebview(message); + + const listener = (event: MessageEvent) => { + const message = event.data; + // Only accept messages from the vscode webview + if (!event.origin.startsWith('vscode-webview://')) { + return; + } + // console.log(`FRONTEND: Message from ${event.origin}: ${JSON.stringify(message)}`); + switch (message.type) { + case MessageTypeNames.ExtensionPong: + console.log('Received WebviewStarted message, will set vsc'); + dispatch(VscodeActions.setVscode()); + + if (message.token) { + const token = JSON.parse(message.token.trim()); + console.log(`FRONTEND: WebviewStarted: ${token}`); + dispatch( + SessionActions.setTokens({ + accessToken: token.accessToken, + refreshToken: token.refreshToken + }) + ); + dispatch(SessionActions.fetchUserAndCourse()); + } + break; + case MessageTypeNames.Text: + const { workspaceLocation, code } = message; + console.log(`FRONTEND: TextMessage: ${code}`); + dispatch(WorkspaceActions.updateEditorValue(workspaceLocation, 0, code)); + break; + case MessageTypeNames.EvalEditor: + dispatch(WorkspaceActions.evalEditor(message.workspaceLocation)); + break; + case MessageTypeNames.Navigate: + window.location.pathname = message.route; + // TODO: Figure out why this doesn't work, this is faster in theory + // navigate(message.route); + break; + case MessageTypeNames.McqQuestion: + dispatch(WorkspaceActions.showMcqPane(message.workspaceLocation, message.options)); + break; + case MessageTypeNames.McqAnswer: + console.log(`FRONTEND: MCQAnswerMessage: ${message}`); + dispatch(SessionActions.submitAnswer(message.questionId, message.choice)); + break; + case MessageTypeNames.AssessmentAnswer: + dispatch(SessionActions.submitAnswer(message.questionId, message.answer)); + break; + } + }; + + window.addEventListener('message', listener); + return () => { + window.removeEventListener('message', listener); + // Reset confirm to the original function + window.confirm = originalConfirm; + }; + }, [dispatch]); +}; From c85a178dcf6392bbb4b93c355d38f63fee949ecc Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Wed, 2 Jul 2025 01:13:42 +0800 Subject: [PATCH 2/5] Rename hook --- .../utils/hooks/{useIsVscode.tsx => useVscodeIntegration.tsx} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/commons/utils/hooks/{useIsVscode.tsx => useVscodeIntegration.tsx} (98%) diff --git a/src/commons/utils/hooks/useIsVscode.tsx b/src/commons/utils/hooks/useVscodeIntegration.tsx similarity index 98% rename from src/commons/utils/hooks/useIsVscode.tsx rename to src/commons/utils/hooks/useVscodeIntegration.tsx index 71b70d5f4a..0100b6c3dd 100644 --- a/src/commons/utils/hooks/useIsVscode.tsx +++ b/src/commons/utils/hooks/useVscodeIntegration.tsx @@ -12,7 +12,7 @@ import Messages, { /** * To handle messages from VS Code. */ -export const useIsVscode = () => { +export const useVscodeIntegration = () => { const dispatch = useDispatch(); useEffect(() => { From 9455347e50756776c5c5d76895cca959a29e93c8 Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Wed, 2 Jul 2025 01:14:05 +0800 Subject: [PATCH 3/5] Use hook in app wrapper for readability --- src/commons/application/Application.tsx | 74 +------------------------ 1 file changed, 2 insertions(+), 72 deletions(-) diff --git a/src/commons/application/Application.tsx b/src/commons/application/Application.tsx index 88f7c52a39..ecd23e3462 100644 --- a/src/commons/application/Application.tsx +++ b/src/commons/application/Application.tsx @@ -1,19 +1,13 @@ import React from 'react'; import { useDispatch } from 'react-redux'; import { Outlet } from 'react-router'; -import Messages, { - MessageType, - MessageTypeNames, - sendToWebview -} from 'src/features/vscode/messages'; import NavigationBar from '../navigationBar/NavigationBar'; import Constants from '../utils/Constants'; import { useLocalStorageState, useSession } from '../utils/Hooks'; -import WorkspaceActions from '../workspace/WorkspaceActions'; +import { useVscodeIntegration } from '../utils/hooks/useVscodeIntegration'; import { defaultWorkspaceSettings, WorkspaceSettingsContext } from '../WorkspaceSettingsContext'; import SessionActions from './actions/SessionActions'; -import VscodeActions from './actions/VscodeActions'; const Application: React.FC = () => { const dispatch = useDispatch(); @@ -77,71 +71,7 @@ const Application: React.FC = () => { }; }, [isPWA, isMobile]); - // Effect to handle messages from VS Code - React.useEffect(() => { - if (!window.confirm) { - // Polyfill confirm() to instead show as VS Code notification - // TODO: Pass text as a new Message to the webview - window.confirm = text => { - console.log(`Confirmation automatically accepted: ${text ?? 'No text provided'}`); - return true; - }; - } - - const message = Messages.ExtensionPing(window.origin); - sendToWebview(message); - - window.addEventListener('message', event => { - const message: MessageType = event.data; - // Only accept messages from the vscode webview - if (!event.origin.startsWith('vscode-webview://')) { - return; - } - // console.log(`FRONTEND: Message from ${event.origin}: ${JSON.stringify(message)}`); - switch (message.type) { - case MessageTypeNames.ExtensionPong: - console.log('Received WebviewStarted message, will set vsc'); - dispatch(VscodeActions.setVscode()); - - if (message.token) { - const token = JSON.parse(message.token.trim()); - console.log(`FRONTEND: WebviewStarted: ${token}`); - dispatch( - SessionActions.setTokens({ - accessToken: token.accessToken, - refreshToken: token.refreshToken - }) - ); - dispatch(SessionActions.fetchUserAndCourse()); - } - break; - case MessageTypeNames.Text: - const { workspaceLocation, code } = message; - console.log(`FRONTEND: TextMessage: ${code}`); - dispatch(WorkspaceActions.updateEditorValue(workspaceLocation, 0, code)); - break; - case MessageTypeNames.EvalEditor: - dispatch(WorkspaceActions.evalEditor(message.workspaceLocation)); - break; - case MessageTypeNames.Navigate: - window.location.pathname = message.route; - // TODO: Figure out why this doesn't work, this is faster in theory - // navigate(message.route); - break; - case MessageTypeNames.McqQuestion: - dispatch(WorkspaceActions.showMcqPane(message.workspaceLocation, message.options)); - break; - case MessageTypeNames.McqAnswer: - console.log(`FRONTEND: MCQAnswerMessage: ${message}`); - dispatch(SessionActions.submitAnswer(message.questionId, message.choice)); - break; - case MessageTypeNames.AssessmentAnswer: - dispatch(SessionActions.submitAnswer(message.questionId, message.answer)); - break; - } - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + useVscodeIntegration(); return ( From e53ec3c625013f19247392b4f30f7a6711d480a8 Mon Sep 17 00:00:00 2001 From: heyzec <61238538+heyzec@users.noreply.github.com> Date: Wed, 2 Jul 2025 01:41:01 +0800 Subject: [PATCH 4/5] use smarter default value of isVscode --- src/commons/application/reducers/VscodeReducer.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/commons/application/reducers/VscodeReducer.ts b/src/commons/application/reducers/VscodeReducer.ts index 50f7fe049c..01b13d9444 100644 --- a/src/commons/application/reducers/VscodeReducer.ts +++ b/src/commons/application/reducers/VscodeReducer.ts @@ -15,6 +15,12 @@ export const VscodeReducer: Reducer = ( const newVscodeReducer = createReducer(defaultVscode, builder => { builder.addCase(VscodeActions.setVscode, state => { - return { ...state, ...{ isVscode: true } }; + return { + ...state, + ...{ + // Quirky way to determine if the application is running in VSCode or not + isVscode: window.parent !== parent + } + }; }); }); From c4cdbdad6296d5e6fc906747d3429e0c0c5ec920 Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Thu, 3 Jul 2025 11:54:05 +0800 Subject: [PATCH 5/5] Use Immer for readability --- src/commons/application/reducers/VscodeReducer.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/commons/application/reducers/VscodeReducer.ts b/src/commons/application/reducers/VscodeReducer.ts index 01b13d9444..23f2d624f1 100644 --- a/src/commons/application/reducers/VscodeReducer.ts +++ b/src/commons/application/reducers/VscodeReducer.ts @@ -15,12 +15,7 @@ export const VscodeReducer: Reducer = ( const newVscodeReducer = createReducer(defaultVscode, builder => { builder.addCase(VscodeActions.setVscode, state => { - return { - ...state, - ...{ - // Quirky way to determine if the application is running in VSCode or not - isVscode: window.parent !== parent - } - }; + // Quirky way to determine if the application is running in VSCode or not + state.isVscode = window.parent !== parent; }); });