diff --git a/package.json b/package.json index 9bdce6f94a..5d28df86bc 100644 --- a/package.json +++ b/package.json @@ -92,7 +92,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", "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz", diff --git a/src/commons/workspace/WorkspaceReducer.ts b/src/commons/workspace/WorkspaceReducer.ts index 5283b9f2a4..c06bb2e1cf 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,16 @@ 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'; + setEditorSessionId, + 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'; import { + addEditorTab, browseReplHistoryDown, browseReplHistoryUp, changeExecTime, @@ -54,43 +49,45 @@ import { endClearContext, evalEditor, evalRepl, + moveCursor, + 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_CHANGEPOINTSTEPS, - UPDATE_CURRENT_ASSESSMENT_ID, - UPDATE_CURRENT_SUBMISSION_ID, - UPDATE_CURRENTSTEP, - UPDATE_EDITOR_BREAKPOINTS, - UPDATE_EDITOR_VALUE, - UPDATE_HAS_UNSAVED_CHANGES, UPDATE_LAST_DEBUGGER_RESULT, UPDATE_LAST_NON_DET_RESULT, - UPDATE_REPL_VALUE, - UPDATE_STEPSTOTAL, - UPDATE_SUBLANGUAGE, - UPDATE_SUBMISSIONS_TABLE_FILTERS, - UPDATE_WORKSPACE, WorkspaceLocation, WorkspaceManagerState } from './WorkspaceTypes'; @@ -366,60 +363,26 @@ 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; + state[workspaceLocation].isRunning = false; + }) + .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, @@ -433,21 +396,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. @@ -455,68 +415,39 @@ 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]: { @@ -525,11 +456,15 @@ const oldWorkspaceReducer: Reducer = ( ...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]: { @@ -537,123 +472,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) { @@ -664,20 +538,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]; @@ -686,15 +555,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!'); @@ -703,21 +567,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!'); @@ -726,21 +579,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!'); @@ -749,21 +592,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) { @@ -773,21 +605,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!'); @@ -796,21 +617,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; @@ -819,13 +629,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 = { @@ -834,23 +639,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!'); @@ -880,16 +675,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!'); @@ -908,16 +698,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; @@ -925,7 +710,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 @@ -938,16 +723,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; @@ -960,7 +740,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; @@ -975,37 +755,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; @@ -1018,23 +782,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]: { @@ -1042,19 +799,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]: { @@ -1062,7 +817,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]: { @@ -1070,7 +829,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]: { @@ -1078,6 +841,27 @@ const oldWorkspaceReducer: Reducer = ( 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; + // }); +}); + +/** Temporarily kept to prevent conflicts */ +const oldWorkspaceReducer: Reducer = ( + state = defaultWorkspaceManager, + action +) => { + const workspaceLocation = getWorkspaceLocation(action); + + switch (action.type) { case UPDATE_CHANGEPOINTSTEPS: return { ...state, 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 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: {} }); }); 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 diff --git a/yarn.lock b/yarn.lock index 5cb9a8fc13..caff454791 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13124,11 +13124,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"