From 0d30ac3a6a9558acf32f929e3f13d43cdbb992ec Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Sun, 24 Mar 2024 13:48:55 +0800 Subject: [PATCH 1/8] Migrate remaining workspace reducers to RTK --- src/commons/workspace/WorkspaceReducer.ts | 776 ++++++++-------------- 1 file changed, 268 insertions(+), 508 deletions(-) diff --git a/src/commons/workspace/WorkspaceReducer.ts b/src/commons/workspace/WorkspaceReducer.ts index 41a0fa7aa8..4c0f708f0c 100644 --- a/src/commons/workspace/WorkspaceReducer.ts +++ b/src/commons/workspace/WorkspaceReducer.ts @@ -3,11 +3,17 @@ import { stringify } from 'js-slang/dist/utils/stringify'; import { Reducer } from 'redux'; import { SourcecastReducer } from '../../features/sourceRecorder/sourcecast/SourcecastReducer'; -import { SET_IS_EDITOR_READONLY } from '../../features/sourceRecorder/sourcecast/SourcecastTypes'; import { SourcereelReducer } from '../../features/sourceRecorder/sourcereel/SourcereelReducer'; import { logOut } from '../application/actions/CommonsActions'; import { + debuggerReset, + debuggerResume, + endDebuggerPause, + endInterruptExecution, + evalInterpreterError, evalInterpreterSuccess, + evalTestcaseFailure, + evalTestcaseSuccess, handleConsoleLog } from '../application/actions/InterpreterActions'; import { @@ -20,27 +26,15 @@ import { ResultOutput } from '../application/ApplicationTypes'; import { - DEBUG_RESET, - DEBUG_RESUME, - END_DEBUG_PAUSE, - END_INTERRUPT_EXECUTION, - EVAL_INTERPRETER_ERROR, - EVAL_TESTCASE_FAILURE, - EVAL_TESTCASE_SUCCESS, - UPDATE_EDITOR_HIGHLIGHTED_LINES, - UPDATE_EDITOR_HIGHLIGHTED_LINES_CONTROL -} from '../application/types/InterpreterTypes'; -import { Testcase } from '../assessment/AssessmentTypes'; -import { - SET_EDITOR_SESSION_ID, - SET_SESSION_DETAILS, - SET_SHAREDB_CONNECTED -} from '../collabEditing/CollabEditingTypes'; -import { NOTIFY_PROGRAM_EVALUATED } from '../sideContent/SideContentTypes'; + setEditorSessionId, + setSessionDetails, + setSharedbConnected +} from '../collabEditing/CollabEditingActions'; import { SourceActionType } from '../utils/ActionsHelper'; import Constants from '../utils/Constants'; import { createContext } from '../utils/JsSlangHelper'; import { + addEditorTab, browseReplHistoryDown, browseReplHistoryUp, changeExecTime, @@ -54,43 +48,42 @@ import { endClearContext, evalEditor, evalRepl, + moveCursor, + notifyProgramEvaluated, + removeEditorTab, + removeEditorTabForFile, + removeEditorTabsForDirectory, + renameEditorTabForFile, + renameEditorTabsForDirectory, + resetTestcase, + resetWorkspace, sendReplInputToOutput, - setTokenCount + setEditorBreakpoint, + setEditorHighlightedLines, + setEditorHighlightedLinesControl, + setFolderMode, + setIsEditorReadonly, + setTokenCount, + shiftEditorTab, + toggleEditorAutorun, + toggleUpdateCse, + toggleUsingCse, + toggleUsingSubst, + updateActiveEditorTab, + updateActiveEditorTabIndex, + updateBreakpointSteps, + updateCurrentAssessmentId, + updateCurrentStep, + updateCurrentSubmissionId, + updateEditorValue, + updateHasUnsavedChanges, + updateReplValue, + updateStepsTotal, + updateSublanguage, + updateSubmissionsTableFilters, + updateWorkspace } from './WorkspaceActions'; -import { - ADD_EDITOR_TAB, - EditorTabState, - MOVE_CURSOR, - REMOVE_EDITOR_TAB, - REMOVE_EDITOR_TAB_FOR_FILE, - REMOVE_EDITOR_TABS_FOR_DIRECTORY, - RENAME_EDITOR_TAB_FOR_FILE, - RENAME_EDITOR_TABS_FOR_DIRECTORY, - RESET_TESTCASE, - RESET_WORKSPACE, - SET_FOLDER_MODE, - SHIFT_EDITOR_TAB, - TOGGLE_EDITOR_AUTORUN, - TOGGLE_UPDATE_CSE, - TOGGLE_USING_CSE, - TOGGLE_USING_SUBST, - UPDATE_ACTIVE_EDITOR_TAB, - UPDATE_ACTIVE_EDITOR_TAB_INDEX, - UPDATE_BREAKPOINTSTEPS, - UPDATE_CURRENT_ASSESSMENT_ID, - UPDATE_CURRENT_SUBMISSION_ID, - UPDATE_CURRENTSTEP, - UPDATE_EDITOR_BREAKPOINTS, - UPDATE_EDITOR_VALUE, - UPDATE_HAS_UNSAVED_CHANGES, - UPDATE_REPL_VALUE, - UPDATE_STEPSTOTAL, - UPDATE_SUBLANGUAGE, - UPDATE_SUBMISSIONS_TABLE_FILTERS, - UPDATE_WORKSPACE, - WorkspaceLocation, - WorkspaceManagerState -} from './WorkspaceTypes'; +import { EditorTabState, WorkspaceLocation, WorkspaceManagerState } from './WorkspaceTypes'; const getWorkspaceLocation = (action: any): WorkspaceLocation => { return action.payload ? action.payload.workspaceLocation : 'assessment'; @@ -363,60 +356,25 @@ const newWorkspaceReducer = createReducer(defaultWorkspaceManager, builder => { state[workspaceLocation].output = newOutput; state[workspaceLocation].isRunning = false; - }); -}); + }) + .addCase(evalTestcaseSuccess, (state, action) => { + const workspaceLocation = getWorkspaceLocation(action); + const testcase = state[workspaceLocation].editorTestcases[action.payload.index]; + testcase.result = action.payload.value; + testcase.errors = undefined; + }) + .addCase(evalTestcaseFailure, (state, action) => { + const workspaceLocation = getWorkspaceLocation(action); + const testcase = state[workspaceLocation].editorTestcases[action.payload.index]; + testcase.result = undefined; + testcase.errors = action.payload.value; + }) + .addCase(evalInterpreterError, (state, action) => { + const workspaceLocation = getWorkspaceLocation(action); -const oldWorkspaceReducer: Reducer = ( - state = defaultWorkspaceManager, - action -) => { - const workspaceLocation = getWorkspaceLocation(action); - let newOutput: InterpreterOutput[]; - let lastOutput: InterpreterOutput; + const lastOutput: InterpreterOutput = state[workspaceLocation].output.slice(-1)[0]; + let newOutput: InterpreterOutput[]; - switch (action.type) { - case EVAL_TESTCASE_SUCCESS: - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - editorTestcases: state[workspaceLocation].editorTestcases.map( - (testcase: Testcase, i: any) => { - if (i === action.payload.index) { - return { - ...testcase, - result: action.payload.value, - errors: undefined - }; - } else { - return testcase; - } - } - ), - isRunning: false - } - }; - case EVAL_TESTCASE_FAILURE: - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - editorTestcases: state[workspaceLocation].editorTestcases.map( - (testcase: Testcase, i: number) => { - if (i === action.payload.index) { - return { - ...testcase, - result: undefined, - errors: action.payload.value - }; - } - return testcase; - } - ) - } - }; - case EVAL_INTERPRETER_ERROR: - lastOutput = state[workspaceLocation].output.slice(-1)[0]; if (lastOutput !== undefined && lastOutput.type === 'running') { newOutput = state[workspaceLocation].output.slice(0, -1).concat({ type: action.payload.type, @@ -430,21 +388,18 @@ const oldWorkspaceReducer: Reducer = ( consoleLogs: [] } as ErrorOutput); } - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - output: newOutput, - isRunning: false, - isDebugging: false - } - }; + + state[workspaceLocation].output = newOutput; + state[workspaceLocation].isRunning = false; + state[workspaceLocation].isDebugging = false; + }) /** * Called to signal the end of an interruption, * i.e called after the interpreter is told to stop interruption, * to cause UI changes. */ - case END_INTERRUPT_EXECUTION: + .addCase(endInterruptExecution, (state, action) => { + const workspaceLocation = getWorkspaceLocation(action); /** * Set the isRunning property of the * context to false, to ensure a re-render. @@ -452,81 +407,56 @@ const oldWorkspaceReducer: Reducer = ( * function does not finish interrupting before * this action is called. */ - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - isRunning: false, - isDebugging: false - } - }; - case END_DEBUG_PAUSE: - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - isRunning: false, - isDebugging: true - } - }; - case DEBUG_RESUME: - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - isRunning: true, - isDebugging: false - } - }; - case DEBUG_RESET: - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - isRunning: false, - isDebugging: false - } - }; - case RESET_TESTCASE: - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - editorTestcases: state[workspaceLocation].editorTestcases.map( - (testcase: Testcase, i: any) => { - if (i === action.payload.index) { - return { - ...testcase, - result: undefined, - errors: undefined - }; - } else { - return testcase; - } - } - ) - } - }; - + state[workspaceLocation].isRunning = false; + state[workspaceLocation].isDebugging = false; + }) + .addCase(endDebuggerPause, (state, action) => { + const workspaceLocation = getWorkspaceLocation(action); + state[workspaceLocation].isRunning = false; + state[workspaceLocation].isDebugging = true; + }) + .addCase(debuggerResume, (state, action) => { + const workspaceLocation = getWorkspaceLocation(action); + state[workspaceLocation].isRunning = true; + state[workspaceLocation].isDebugging = false; + }) + .addCase(debuggerReset, (state, action) => { + const workspaceLocation = getWorkspaceLocation(action); + state[workspaceLocation].isRunning = false; + state[workspaceLocation].isDebugging = false; + }) + .addCase(resetTestcase, (state, action) => { + const workspaceLocation = getWorkspaceLocation(action); + const testcase = state[workspaceLocation].editorTestcases[action.payload.index]; + testcase.result = undefined; + testcase.errors = undefined; + }) /** * Resets the workspace to default settings, * including the js-slang Context. Apply * any specified settings (workspaceOptions) */ - case RESET_WORKSPACE: + .addCase(resetWorkspace, (state, action) => { + const workspaceLocation = getWorkspaceLocation(action); + // For some reason mutating the state directly results in type + // errors, so we have to do it the old-fashioned way return { ...state, [workspaceLocation]: { - ...state[workspaceLocation], + // ...state[workspaceLocation], ...createDefaultWorkspace(workspaceLocation), ...action.payload.workspaceOptions } }; + }) /** * Updates workspace without changing anything * which has not been specified */ - case UPDATE_WORKSPACE: + .addCase(updateWorkspace, (state, action) => { + const workspaceLocation = getWorkspaceLocation(action); + // For some reason mutating the state directly results in type + // errors, so we have to do it the old-fashioned way return { ...state, [workspaceLocation]: { @@ -534,123 +464,62 @@ const oldWorkspaceReducer: Reducer = ( ...action.payload.workspaceOptions } }; - case SET_EDITOR_SESSION_ID: - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - editorSessionId: action.payload.editorSessionId - } - }; - case SET_SESSION_DETAILS: - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - sessionDetails: action.payload.sessionDetails - } - }; - case SET_IS_EDITOR_READONLY: - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - isEditorReadonly: action.payload.isEditorReadonly - } - }; - case SET_SHAREDB_CONNECTED: - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - sharedbConnected: action.payload.connected - } - }; - case TOGGLE_EDITOR_AUTORUN: - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - isEditorAutorun: !state[workspaceLocation].isEditorAutorun - } - }; - case TOGGLE_USING_SUBST: { + }) + .addCase(setEditorSessionId, (state, action) => { + const workspaceLocation = getWorkspaceLocation(action); + state[workspaceLocation].editorSessionId = action.payload.editorSessionId; + }) + .addCase(setSessionDetails, (state, action) => { + const workspaceLocation = getWorkspaceLocation(action); + state[workspaceLocation].sessionDetails = action.payload.sessionDetails; + }) + .addCase(setIsEditorReadonly, (state, action) => { + const workspaceLocation = getWorkspaceLocation(action); + state[workspaceLocation].isEditorReadonly = action.payload.isEditorReadonly; + }) + .addCase(setSharedbConnected, (state, action) => { + const workspaceLocation = getWorkspaceLocation(action); + state[workspaceLocation].sharedbConnected = action.payload.connected; + }) + .addCase(toggleEditorAutorun, (state, action) => { + const workspaceLocation = getWorkspaceLocation(action); + state[workspaceLocation].isEditorAutorun = !state[workspaceLocation].isEditorAutorun; + }) + .addCase(toggleUsingSubst, (state, action) => { const { workspaceLocation } = action.payload; if (workspaceLocation === 'playground' || workspaceLocation === 'sicp') { - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - usingSubst: action.payload.usingSubst - } - }; - } else { - return state; + state[workspaceLocation].usingSubst = action.payload.usingSubst; } - } - case TOGGLE_USING_CSE: { + }) + .addCase(toggleUsingCse, (state, action) => { const { workspaceLocation } = action.payload; if (workspaceLocation === 'playground' || workspaceLocation === 'sicp') { - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - usingCse: action.payload.usingCse - } - }; - } else { - return state; + state[workspaceLocation].usingCse = action.payload.usingCse; } - } - case TOGGLE_UPDATE_CSE: { + }) + .addCase(toggleUpdateCse, (state, action) => { const { workspaceLocation } = action.payload; if (workspaceLocation === 'playground' || workspaceLocation === 'sicp') { - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - updateCse: action.payload.updateCse - } - }; - } else { - return state; + state[workspaceLocation].updateCse = action.payload.updateCse; } - } - case UPDATE_SUBMISSIONS_TABLE_FILTERS: - return { - ...state, - grading: { - ...state.grading, - submissionsTableFilters: action.payload.filters - } - }; - case UPDATE_CURRENT_ASSESSMENT_ID: - return { - ...state, - assessment: { - ...state.assessment, - currentAssessment: action.payload.assessmentId, - currentQuestion: action.payload.questionId - } - }; - case UPDATE_CURRENT_SUBMISSION_ID: - return { - ...state, - grading: { - ...state.grading, - currentSubmission: action.payload.submissionId, - currentQuestion: action.payload.questionId - } - }; - case SET_FOLDER_MODE: - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - isFolderModeEnabled: action.payload.isFolderModeEnabled - } - }; - case UPDATE_ACTIVE_EDITOR_TAB_INDEX: { + }) + .addCase(updateSubmissionsTableFilters, (state, action) => { + state.grading.submissionsTableFilters = action.payload.filters; + }) + .addCase(updateCurrentAssessmentId, (state, action) => { + state.assessment.currentAssessment = action.payload.assessmentId; + state.assessment.currentQuestion = action.payload.questionId; + }) + .addCase(updateCurrentSubmissionId, (state, action) => { + state.grading.currentSubmission = action.payload.submissionId; + state.grading.currentQuestion = action.payload.questionId; + }) + .addCase(setFolderMode, (state, action) => { + const workspaceLocation = getWorkspaceLocation(action); + state[workspaceLocation].isFolderModeEnabled = action.payload.isFolderModeEnabled; + }) + .addCase(updateActiveEditorTabIndex, (state, action) => { + const workspaceLocation = getWorkspaceLocation(action); const activeEditorTabIndex = action.payload.activeEditorTabIndex; if (activeEditorTabIndex !== null) { if (activeEditorTabIndex < 0) { @@ -661,20 +530,15 @@ const oldWorkspaceReducer: Reducer = ( } } - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - activeEditorTabIndex: activeEditorTabIndex - } - }; - } - case UPDATE_ACTIVE_EDITOR_TAB: { + state[workspaceLocation].activeEditorTabIndex = activeEditorTabIndex; + }) + .addCase(updateActiveEditorTab, (state, action) => { + const workspaceLocation = getWorkspaceLocation(action); const { activeEditorTabOptions } = action.payload; const activeEditorTabIndex = state[workspaceLocation].activeEditorTabIndex; // Do not modify the workspace state if there is no active editor tab. if (activeEditorTabIndex === null) { - return state; + return; } const updatedEditorTabs = [...state[workspaceLocation].editorTabs]; @@ -683,15 +547,10 @@ const oldWorkspaceReducer: Reducer = ( ...activeEditorTabOptions }; - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - editorTabs: updatedEditorTabs - } - }; - } - case UPDATE_EDITOR_VALUE: { + state[workspaceLocation].editorTabs = updatedEditorTabs; + }) + .addCase(updateEditorValue, (state, action) => { + const workspaceLocation = getWorkspaceLocation(action); const { editorTabIndex, newEditorValue } = action.payload; if (editorTabIndex < 0) { throw new Error('Editor tab index must be non-negative!'); @@ -700,21 +559,10 @@ const oldWorkspaceReducer: Reducer = ( throw new Error('Editor tab index must have a corresponding editor tab!'); } - const newEditorTabs = [...state[workspaceLocation].editorTabs]; - newEditorTabs[editorTabIndex] = { - ...newEditorTabs[editorTabIndex], - value: newEditorValue - }; - - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - editorTabs: newEditorTabs - } - }; - } - case UPDATE_EDITOR_BREAKPOINTS: { + state[workspaceLocation].editorTabs[editorTabIndex].value = newEditorValue; + }) + .addCase(setEditorBreakpoint, (state, action) => { + const workspaceLocation = getWorkspaceLocation(action); const { editorTabIndex, newBreakpoints } = action.payload; if (editorTabIndex < 0) { throw new Error('Editor tab index must be non-negative!'); @@ -723,21 +571,11 @@ const oldWorkspaceReducer: Reducer = ( throw new Error('Editor tab index must have a corresponding editor tab!'); } - const newEditorTabs = [...state[workspaceLocation].editorTabs]; - newEditorTabs[editorTabIndex] = { - ...newEditorTabs[editorTabIndex], - breakpoints: newBreakpoints - }; - - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - editorTabs: newEditorTabs - } - }; - } - case UPDATE_EDITOR_HIGHLIGHTED_LINES: { + state[workspaceLocation].editorTabs[editorTabIndex].breakpoints = newBreakpoints; + }) + .addCase(setEditorHighlightedLines, (state, action) => { + // TODO: This and the subsequent reducer achieves the same thing? + const workspaceLocation = getWorkspaceLocation(action); const { editorTabIndex, newHighlightedLines } = action.payload; if (editorTabIndex < 0) { throw new Error('Editor tab index must be non-negative!'); @@ -746,21 +584,10 @@ const oldWorkspaceReducer: Reducer = ( throw new Error('Editor tab index must have a corresponding editor tab!'); } - const newEditorTabs = [...state[workspaceLocation].editorTabs]; - newEditorTabs[editorTabIndex] = { - ...newEditorTabs[editorTabIndex], - highlightedLines: newHighlightedLines - }; - - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - editorTabs: newEditorTabs - } - }; - } - case UPDATE_EDITOR_HIGHLIGHTED_LINES_CONTROL: { + state[workspaceLocation].editorTabs[editorTabIndex].highlightedLines = newHighlightedLines; + }) + .addCase(setEditorHighlightedLinesControl, (state, action) => { + const workspaceLocation = getWorkspaceLocation(action); const { editorTabIndex, newHighlightedLines } = action.payload; if (editorTabIndex < 0) { @@ -770,21 +597,10 @@ const oldWorkspaceReducer: Reducer = ( throw new Error('Editor tab index must have a corresponding editor tab!'); } - const newEditorTabs = [...state[workspaceLocation].editorTabs]; - newEditorTabs[editorTabIndex] = { - ...newEditorTabs[editorTabIndex], - highlightedLines: newHighlightedLines - }; - - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - editorTabs: newEditorTabs - } - }; - } - case MOVE_CURSOR: { + state[workspaceLocation].editorTabs[editorTabIndex].highlightedLines = newHighlightedLines; + }) + .addCase(moveCursor, (state, action) => { + const workspaceLocation = getWorkspaceLocation(action); const { editorTabIndex, newCursorPosition } = action.payload; if (editorTabIndex < 0) { throw new Error('Editor tab index must be non-negative!'); @@ -793,21 +609,10 @@ const oldWorkspaceReducer: Reducer = ( throw new Error('Editor tab index must have a corresponding editor tab!'); } - const newEditorTabs = [...state[workspaceLocation].editorTabs]; - newEditorTabs[editorTabIndex] = { - ...newEditorTabs[editorTabIndex], - newCursorPosition - }; - - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - editorTabs: newEditorTabs - } - }; - } - case ADD_EDITOR_TAB: { + state[workspaceLocation].editorTabs[editorTabIndex].newCursorPosition = newCursorPosition; + }) + .addCase(addEditorTab, (state, action) => { + const workspaceLocation = getWorkspaceLocation(action); const { filePath, editorValue } = action.payload; const editorTabs = state[workspaceLocation].editorTabs; @@ -816,13 +621,8 @@ const oldWorkspaceReducer: Reducer = ( ); const fileIsAlreadyOpen = openedEditorTabIndex !== -1; if (fileIsAlreadyOpen) { - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - activeEditorTabIndex: openedEditorTabIndex - } - }; + state[workspaceLocation].activeEditorTabIndex = openedEditorTabIndex; + return; } const newEditorTab: EditorTabState = { @@ -831,23 +631,13 @@ const oldWorkspaceReducer: Reducer = ( highlightedLines: [], breakpoints: [] }; - const newEditorTabs: EditorTabState[] = [ - ...state[workspaceLocation].editorTabs, - newEditorTab - ]; + editorTabs.push(newEditorTab); // Set the newly added editor tab as the active tab. - const newActiveEditorTabIndex = newEditorTabs.length - 1; - - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - activeEditorTabIndex: newActiveEditorTabIndex, - editorTabs: newEditorTabs - } - }; - } - case SHIFT_EDITOR_TAB: { + const newActiveEditorTabIndex = editorTabs.length - 1; + state[workspaceLocation].activeEditorTabIndex = newActiveEditorTabIndex; + }) + .addCase(shiftEditorTab, (state, action) => { + const workspaceLocation = getWorkspaceLocation(action); const { previousEditorTabIndex, newEditorTabIndex } = action.payload; if (previousEditorTabIndex < 0) { throw new Error('Previous editor tab index must be non-negative!'); @@ -877,16 +667,11 @@ const oldWorkspaceReducer: Reducer = ( ...filteredEditorTabs.slice(newEditorTabIndex) ]; - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - activeEditorTabIndex: newActiveEditorTabIndex, - editorTabs: newEditorTabs - } - }; - } - case REMOVE_EDITOR_TAB: { + state[workspaceLocation].activeEditorTabIndex = newActiveEditorTabIndex; + state[workspaceLocation].editorTabs = newEditorTabs; + }) + .addCase(removeEditorTab, (state, action) => { + const workspaceLocation = getWorkspaceLocation(action); const editorTabIndex = action.payload.editorTabIndex; if (editorTabIndex < 0) { throw new Error('Editor tab index must be non-negative!'); @@ -905,16 +690,11 @@ const oldWorkspaceReducer: Reducer = ( newEditorTabs.length ); - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - activeEditorTabIndex: newActiveEditorTabIndex, - editorTabs: newEditorTabs - } - }; - } - case REMOVE_EDITOR_TAB_FOR_FILE: { + state[workspaceLocation].activeEditorTabIndex = newActiveEditorTabIndex; + state[workspaceLocation].editorTabs = newEditorTabs; + }) + .addCase(removeEditorTabForFile, (state, action) => { + const workspaceLocation = getWorkspaceLocation(action); const removedFilePath = action.payload.removedFilePath; const editorTabs = state[workspaceLocation].editorTabs; @@ -922,7 +702,7 @@ const oldWorkspaceReducer: Reducer = ( (editorTab: EditorTabState) => editorTab.filePath === removedFilePath ); if (editorTabIndexToRemove === -1) { - return state; + return; } const newEditorTabs = editorTabs.filter( (editorTab: EditorTabState, index: number) => index !== editorTabIndexToRemove @@ -935,16 +715,11 @@ const oldWorkspaceReducer: Reducer = ( newEditorTabs.length ); - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - activeEditorTabIndex: newActiveEditorTabIndex, - editorTabs: newEditorTabs - } - }; - } - case REMOVE_EDITOR_TABS_FOR_DIRECTORY: { + state[workspaceLocation].activeEditorTabIndex = newActiveEditorTabIndex; + state[workspaceLocation].editorTabs = newEditorTabs; + }) + .addCase(removeEditorTabsForDirectory, (state, action) => { + const workspaceLocation = getWorkspaceLocation(action); const removedDirectoryPath = action.payload.removedDirectoryPath; const editorTabs = state[workspaceLocation].editorTabs; @@ -957,7 +732,7 @@ const oldWorkspaceReducer: Reducer = ( }) .filter((index: number | null): index is number => index !== null); if (editorTabIndicesToRemove.length === 0) { - return state; + return; } let newActiveEditorTabIndex = state[workspaceLocation].activeEditorTabIndex; @@ -972,37 +747,21 @@ const oldWorkspaceReducer: Reducer = ( ); } - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - activeEditorTabIndex: newActiveEditorTabIndex, - editorTabs: newEditorTabs - } - }; - } - case RENAME_EDITOR_TAB_FOR_FILE: { + state[workspaceLocation].activeEditorTabIndex = newActiveEditorTabIndex; + state[workspaceLocation].editorTabs = newEditorTabs; + }) + .addCase(renameEditorTabForFile, (state, action) => { + const workspaceLocation = getWorkspaceLocation(action); const { oldFilePath, newFilePath } = action.payload; const editorTabs = state[workspaceLocation].editorTabs; - const newEditorTabs = editorTabs.map((editorTab: EditorTabState) => - editorTab.filePath === oldFilePath - ? { - ...editorTab, - filePath: newFilePath - } - : editorTab - ); - - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - editorTabs: newEditorTabs - } - }; - } - case RENAME_EDITOR_TABS_FOR_DIRECTORY: { + const tabToEdit = editorTabs.find(({ filePath }) => filePath === oldFilePath); + if (tabToEdit) { + tabToEdit.filePath = newFilePath; + } + }) + .addCase(renameEditorTabsForDirectory, (state, action) => { + const workspaceLocation = getWorkspaceLocation(action); const { oldDirectoryPath, newDirectoryPath } = action.payload; const editorTabs = state[workspaceLocation].editorTabs; @@ -1015,23 +774,16 @@ const oldWorkspaceReducer: Reducer = ( : editorTab ); - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - editorTabs: newEditorTabs - } - }; - } - case UPDATE_REPL_VALUE: - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - replValue: action.payload.newReplValue - } - }; - case UPDATE_HAS_UNSAVED_CHANGES: + state[workspaceLocation].editorTabs = newEditorTabs; + }) + .addCase(updateReplValue, (state, action) => { + const workspaceLocation = getWorkspaceLocation(action); + state[workspaceLocation].replValue = action.payload.newReplValue; + }) + .addCase(updateHasUnsavedChanges, (state, action) => { + // For some reason mutating the state directly results in type + // errors, so we have to do it the old-fashioned way + const workspaceLocation = getWorkspaceLocation(action); return { ...state, [workspaceLocation]: { @@ -1039,19 +791,17 @@ const oldWorkspaceReducer: Reducer = ( hasUnsavedChanges: action.payload.hasUnsavedChanges } }; - case UPDATE_SUBLANGUAGE: - return { - ...state, - playground: { - ...state.playground, - context: { - ...state.playground.context, - chapter: action.payload.sublang.chapter, - variant: action.payload.sublang.variant - } - } - }; - case UPDATE_CURRENTSTEP: + }) + .addCase(updateSublanguage, (state, action) => { + // TODO: Mark for removal + const { chapter, variant } = action.payload.sublang; + state.playground.context.chapter = chapter; + state.playground.context.variant = variant; + }) + .addCase(updateCurrentStep, (state, action) => { + // For some reason mutating the state directly results in type + // errors, so we have to do it the old-fashioned way + const workspaceLocation = getWorkspaceLocation(action); return { ...state, [workspaceLocation]: { @@ -1059,7 +809,11 @@ const oldWorkspaceReducer: Reducer = ( currentStep: action.payload.steps } }; - case UPDATE_STEPSTOTAL: + }) + .addCase(updateStepsTotal, (state, action) => { + // For some reason mutating the state directly results in type + // errors, so we have to do it the old-fashioned way + const workspaceLocation = getWorkspaceLocation(action); return { ...state, [workspaceLocation]: { @@ -1067,7 +821,11 @@ const oldWorkspaceReducer: Reducer = ( stepsTotal: action.payload.steps } }; - case UPDATE_BREAKPOINTSTEPS: + }) + .addCase(updateBreakpointSteps, (state, action) => { + // For some reason mutating the state directly results in type + // errors, so we have to do it the old-fashioned way + const workspaceLocation = getWorkspaceLocation(action); return { ...state, [workspaceLocation]: { @@ -1075,23 +833,25 @@ const oldWorkspaceReducer: Reducer = ( breakpointSteps: action.payload.breakpointSteps } }; - case NOTIFY_PROGRAM_EVALUATED: { - const debuggerContext = { - ...state[workspaceLocation].debuggerContext, - result: action.payload.result, - lastDebuggerResult: action.payload.lastDebuggerResult, - code: action.payload.code, - context: action.payload.context, - workspaceLocation: action.payload.workspaceLocation - }; - return { - ...state, - [workspaceLocation]: { - ...state[workspaceLocation], - debuggerContext - } - }; - } + }) + .addCase(notifyProgramEvaluated, (state, action) => { + const workspaceLocation = getWorkspaceLocation(action); + + const debuggerContext = state[workspaceLocation].debuggerContext; + debuggerContext.result = action.payload.result; + debuggerContext.lastDebuggerResult = action.payload.lastDebuggerResult; + debuggerContext.code = action.payload.code; + debuggerContext.context = action.payload.context; + debuggerContext.workspaceLocation = action.payload.workspaceLocation; + }); +}); + +/** Temporarily kept to prevent conflicts */ +const oldWorkspaceReducer: Reducer = ( + state = defaultWorkspaceManager, + action +) => { + switch (action.type) { default: return state; } From 58d1a711d64e0ca5992ca05678331a5c4f581027 Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Sun, 24 Mar 2024 13:59:44 +0800 Subject: [PATCH 2/8] Migrate remaining action creators to RTK --- src/features/achievement/AchievementActions.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/features/achievement/AchievementActions.ts b/src/features/achievement/AchievementActions.ts index 1c6f711f9b..c62bd98a25 100644 --- a/src/features/achievement/AchievementActions.ts +++ b/src/features/achievement/AchievementActions.ts @@ -1,6 +1,5 @@ import { createAction } from '@reduxjs/toolkit'; import { AssessmentOverview } from 'src/commons/assessment/AssessmentTypes'; -import { action } from 'typesafe-actions'; import { AchievementGoal, @@ -37,7 +36,7 @@ export const bulkUpdateGoals = createAction(BULK_UPDATE_GOALS, (goals: GoalDefin payload: goals })); -export const getAchievements = () => action(GET_ACHIEVEMENTS); +export const getAchievements = createAction(GET_ACHIEVEMENTS, () => ({ payload: {} })); export const getGoals = createAction(GET_GOALS, (studentCourseRegId: number) => ({ payload: studentCourseRegId From 57948c5fc1d9d5dd5a2419d722287b78bcf6bf92 Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Sun, 24 Mar 2024 14:00:44 +0800 Subject: [PATCH 3/8] Remove `typesafe-actions` dependency Type safety is now achieved via Redux Toolkit. --- package.json | 1 - yarn.lock | 5 ----- 2 files changed, 6 deletions(-) diff --git a/package.json b/package.json index dd4d4d2594..a431600552 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,6 @@ "rehype-react": "^8.0.0", "showdown": "^2.1.0", "sourceror": "^0.8.5", - "typesafe-actions": "^5.1.0", "unified": "^11.0.0", "uuid": "^9.0.0", "xml2js": "^0.6.0", diff --git a/yarn.lock b/yarn.lock index 03896564ba..a7ca1ea717 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12771,11 +12771,6 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== -typesafe-actions@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/typesafe-actions/-/typesafe-actions-5.1.0.tgz#9afe8b1e6a323af1fd59e6a57b11b7dd6623d2f1" - integrity sha512-bna6Yi1pRznoo6Bz1cE6btB/Yy8Xywytyfrzu/wc+NFW3ZF0I+2iCGImhBsoYYCOWuICtRO4yHcnDlzgo1AdNg== - typescript-compare@^0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/typescript-compare/-/typescript-compare-0.0.2.tgz#7ee40a400a406c2ea0a7e551efd3309021d5f425" From 26a725d93e95879250f50eaf1755b4dd5b6886e5 Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Sun, 24 Mar 2024 14:02:48 +0800 Subject: [PATCH 4/8] Update testcase --- src/features/achievement/__tests__/AchievementActions.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/features/achievement/__tests__/AchievementActions.ts b/src/features/achievement/__tests__/AchievementActions.ts index cd3db6dd4a..a4fcdfc379 100644 --- a/src/features/achievement/__tests__/AchievementActions.ts +++ b/src/features/achievement/__tests__/AchievementActions.ts @@ -15,7 +15,8 @@ test('getAchievements generates correct action object', () => { const action = getAchievements(); expect(action).toEqual({ - type: GET_ACHIEVEMENTS + type: GET_ACHIEVEMENTS, + payload: {} }); }); From e7ef1e0da87fec8d33457dacfb03afc52d6adfb5 Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Sun, 24 Mar 2024 14:12:26 +0800 Subject: [PATCH 5/8] Fix format --- src/commons/workspace/WorkspaceReducer.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/commons/workspace/WorkspaceReducer.ts b/src/commons/workspace/WorkspaceReducer.ts index f9925989eb..eeb313c0ee 100644 --- a/src/commons/workspace/WorkspaceReducer.ts +++ b/src/commons/workspace/WorkspaceReducer.ts @@ -84,8 +84,11 @@ import { updateWorkspace } from './WorkspaceActions'; import { - EditorTabState, UPDATE_LAST_DEBUGGER_RESULT, - UPDATE_LAST_NON_DET_RESULT, WorkspaceLocation, WorkspaceManagerState + EditorTabState, + UPDATE_LAST_DEBUGGER_RESULT, + UPDATE_LAST_NON_DET_RESULT, + WorkspaceLocation, + WorkspaceManagerState } from './WorkspaceTypes'; const getWorkspaceLocation = (action: any): WorkspaceLocation => { From 820c98066a48dbdf760cbb0be5a71141f4e9683c Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Sun, 24 Mar 2024 14:19:37 +0800 Subject: [PATCH 6/8] Fix compile error --- src/pages/createStore.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/createStore.ts b/src/pages/createStore.ts index 48962a4a3e..04389b95bb 100644 --- a/src/pages/createStore.ts +++ b/src/pages/createStore.ts @@ -2,6 +2,7 @@ import { configureStore } from '@reduxjs/toolkit'; import { setAutoFreeze } from 'immer'; import { throttle } from 'lodash'; import createSagaMiddleware from 'redux-saga'; +import { SourceActionType } from 'src/commons/utils/ActionsHelper'; import { defaultState, OverallState } from '../commons/application/ApplicationTypes'; import createRootReducer from '../commons/application/reducers/RootReducer'; @@ -19,7 +20,7 @@ export function createStore() { const middleware = [sagaMiddleware]; const initialStore = loadStore(loadStoredState()) || defaultState; - const createdStore = configureStore({ + const createdStore = configureStore({ reducer: createRootReducer(), // Fix for redux-saga type incompatibility // See: https://github.com/reduxjs/redux-toolkit/issues/3950 From 0d10a878d213059bf6ec702948ae9d850831fcb8 Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Thu, 11 Apr 2024 03:00:24 +0800 Subject: [PATCH 7/8] Fix errors --- src/commons/workspace/WorkspaceReducer.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/commons/workspace/WorkspaceReducer.ts b/src/commons/workspace/WorkspaceReducer.ts index fd91010384..58114faabc 100644 --- a/src/commons/workspace/WorkspaceReducer.ts +++ b/src/commons/workspace/WorkspaceReducer.ts @@ -369,6 +369,7 @@ const newWorkspaceReducer = createReducer(defaultWorkspaceManager, builder => { const testcase = state[workspaceLocation].editorTestcases[action.payload.index]; testcase.result = action.payload.value; testcase.errors = undefined; + state[workspaceLocation].isRunning = false; }) .addCase(evalTestcaseFailure, (state, action) => { const workspaceLocation = getWorkspaceLocation(action); @@ -450,7 +451,7 @@ const newWorkspaceReducer = createReducer(defaultWorkspaceManager, builder => { return { ...state, [workspaceLocation]: { - // ...state[workspaceLocation], + ...state[workspaceLocation], ...createDefaultWorkspace(workspaceLocation), ...action.payload.workspaceOptions } From b5c0eb25d4d2c6a76446a84d58ba7c54f90cf675 Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Sat, 13 Apr 2024 10:38:24 +0800 Subject: [PATCH 8/8] Restore old NOTIFY_PROGRAM_EVALUATED reducer Done to optimize the diff and will be removed later. --- src/commons/workspace/WorkspaceReducer.ts | 39 ++++++++++++++++------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/src/commons/workspace/WorkspaceReducer.ts b/src/commons/workspace/WorkspaceReducer.ts index 58114faabc..c06bb2e1cf 100644 --- a/src/commons/workspace/WorkspaceReducer.ts +++ b/src/commons/workspace/WorkspaceReducer.ts @@ -30,6 +30,7 @@ import { setSessionDetails, setSharedbConnected } from '../collabEditing/CollabEditingActions'; +import { NOTIFY_PROGRAM_EVALUATED } from '../sideContent/SideContentTypes'; import { SourceActionType } from '../utils/ActionsHelper'; import Constants from '../utils/Constants'; import { createContext } from '../utils/JsSlangHelper'; @@ -49,7 +50,6 @@ import { evalEditor, evalRepl, moveCursor, - notifyProgramEvaluated, removeEditorTab, removeEditorTabForFile, removeEditorTabsForDirectory, @@ -841,17 +841,17 @@ const newWorkspaceReducer = createReducer(defaultWorkspaceManager, builder => { breakpointSteps: action.payload.breakpointSteps } }; - }) - .addCase(notifyProgramEvaluated, (state, action) => { - const workspaceLocation = getWorkspaceLocation(action); - - const debuggerContext = state[workspaceLocation].debuggerContext; - debuggerContext.result = action.payload.result; - debuggerContext.lastDebuggerResult = action.payload.lastDebuggerResult; - debuggerContext.code = action.payload.code; - debuggerContext.context = action.payload.context; - debuggerContext.workspaceLocation = action.payload.workspaceLocation; }); + // .addCase(notifyProgramEvaluated, (state, action) => { + // const workspaceLocation = getWorkspaceLocation(action); + + // const debuggerContext = state[workspaceLocation].debuggerContext; + // debuggerContext.result = action.payload.result; + // debuggerContext.lastDebuggerResult = action.payload.lastDebuggerResult; + // debuggerContext.code = action.payload.code; + // debuggerContext.context = action.payload.context; + // debuggerContext.workspaceLocation = action.payload.workspaceLocation; + // }); }); /** Temporarily kept to prevent conflicts */ @@ -887,6 +887,23 @@ const oldWorkspaceReducer: Reducer = ( lastNonDetResult: action.payload.lastNonDetResult } }; + case NOTIFY_PROGRAM_EVALUATED: { + const debuggerContext = { + ...state[workspaceLocation].debuggerContext, + result: action.payload.result, + lastDebuggerResult: action.payload.lastDebuggerResult, + code: action.payload.code, + context: action.payload.context, + workspaceLocation: action.payload.workspaceLocation + }; + return { + ...state, + [workspaceLocation]: { + ...state[workspaceLocation], + debuggerContext + } + }; + } default: return state; }