fix: handle challenge resets through redux (#43843)
Instead of relying on heuristics like "does the current content differ from the initial content?" this just resets if the reset button has been pressed.
This commit is contained in:
committed by
GitHub
parent
91717384fd
commit
6a5f586f73
@ -38,7 +38,9 @@ import {
|
|||||||
updateFile,
|
updateFile,
|
||||||
challengeTestsSelector,
|
challengeTestsSelector,
|
||||||
submitChallenge,
|
submitChallenge,
|
||||||
initTests
|
initTests,
|
||||||
|
isResettingSelector,
|
||||||
|
stopResetting
|
||||||
} from '../redux';
|
} from '../redux';
|
||||||
|
|
||||||
import './editor.css';
|
import './editor.css';
|
||||||
@ -61,11 +63,13 @@ interface EditorProps {
|
|||||||
initTests: (tests: Test[]) => void;
|
initTests: (tests: Test[]) => void;
|
||||||
initialTests: Test[];
|
initialTests: Test[];
|
||||||
isProjectStep: boolean;
|
isProjectStep: boolean;
|
||||||
|
isResetting: boolean;
|
||||||
output: string[];
|
output: string[];
|
||||||
resizeProps: ResizePropsType;
|
resizeProps: ResizePropsType;
|
||||||
saveEditorContent: () => void;
|
saveEditorContent: () => void;
|
||||||
setEditorFocusability: (isFocusable: boolean) => void;
|
setEditorFocusability: (isFocusable: boolean) => void;
|
||||||
submitChallenge: () => void;
|
submitChallenge: () => void;
|
||||||
|
stopResetting: () => void;
|
||||||
tests: Test[];
|
tests: Test[];
|
||||||
theme: string;
|
theme: string;
|
||||||
title: string;
|
title: string;
|
||||||
@ -104,16 +108,19 @@ const mapStateToProps = createSelector(
|
|||||||
canFocusEditorSelector,
|
canFocusEditorSelector,
|
||||||
consoleOutputSelector,
|
consoleOutputSelector,
|
||||||
isDonationModalOpenSelector,
|
isDonationModalOpenSelector,
|
||||||
|
isResettingSelector,
|
||||||
userSelector,
|
userSelector,
|
||||||
challengeTestsSelector,
|
challengeTestsSelector,
|
||||||
(
|
(
|
||||||
canFocus: boolean,
|
canFocus: boolean,
|
||||||
output: string[],
|
output: string[],
|
||||||
open,
|
open,
|
||||||
|
isResetting: boolean,
|
||||||
{ theme = 'default' }: { theme: string },
|
{ theme = 'default' }: { theme: string },
|
||||||
tests: [{ text: string; testString: string }]
|
tests: [{ text: string; testString: string }]
|
||||||
) => ({
|
) => ({
|
||||||
canFocus: open ? false : canFocus,
|
canFocus: open ? false : canFocus,
|
||||||
|
isResetting,
|
||||||
output,
|
output,
|
||||||
theme,
|
theme,
|
||||||
tests
|
tests
|
||||||
@ -128,7 +135,8 @@ const mapDispatchToProps = {
|
|||||||
setEditorFocusability,
|
setEditorFocusability,
|
||||||
updateFile,
|
updateFile,
|
||||||
submitChallenge,
|
submitChallenge,
|
||||||
initTests
|
initTests,
|
||||||
|
stopResetting
|
||||||
};
|
};
|
||||||
|
|
||||||
const modeMap = {
|
const modeMap = {
|
||||||
@ -281,7 +289,7 @@ const Editor = (props: EditorProps): JSX.Element => {
|
|||||||
|
|
||||||
// Updates the model if the contents has changed. This is only necessary for
|
// Updates the model if the contents has changed. This is only necessary for
|
||||||
// changes coming from outside the editor (such as code resets).
|
// changes coming from outside the editor (such as code resets).
|
||||||
const updateEditorValues = () => {
|
const resetEditorValues = () => {
|
||||||
const { challengeFiles, fileKey } = props;
|
const { challengeFiles, fileKey } = props;
|
||||||
const { model } = data;
|
const { model } = data;
|
||||||
|
|
||||||
@ -290,9 +298,6 @@ const Editor = (props: EditorProps): JSX.Element => {
|
|||||||
)?.contents;
|
)?.contents;
|
||||||
if (model?.getValue() !== newContents) {
|
if (model?.getValue() !== newContents) {
|
||||||
model?.setValue(newContents ?? '');
|
model?.setValue(newContents ?? '');
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1001,21 +1006,24 @@ const Editor = (props: EditorProps): JSX.Element => {
|
|||||||
// editor.
|
// editor.
|
||||||
const { editor } = data;
|
const { editor } = data;
|
||||||
|
|
||||||
const hasChangedContents = updateEditorValues();
|
if (props.isResetting) {
|
||||||
if (hasChangedContents && hasEditableRegion()) {
|
// NOTE: this looks a lot like a race condition, since stopResetting gets
|
||||||
initializeDescriptionAndOutputWidgets();
|
// called in each editor and changes isResetting. However, all open editor
|
||||||
updateDescriptionZone();
|
// are rendered in a batch (before stopResetting talks to redux), so they
|
||||||
updateOutputZone();
|
// all get to this point. Also stopResetting is idempotent, so it doesn't
|
||||||
}
|
// matter that each editor calls it.
|
||||||
|
props.stopResetting();
|
||||||
if (hasChangedContents) {
|
resetEditorValues();
|
||||||
focusIfTargetEditor();
|
focusIfTargetEditor();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.initialTests) initTests(props.initialTests);
|
if (props.initialTests) initTests(props.initialTests);
|
||||||
|
|
||||||
if (hasEditableRegion() && editor) {
|
if (hasEditableRegion() && editor) {
|
||||||
if (hasChangedContents) {
|
if (props.isResetting) {
|
||||||
|
initializeDescriptionAndOutputWidgets();
|
||||||
|
updateDescriptionZone();
|
||||||
|
updateOutputZone();
|
||||||
showEditableRegion(editor);
|
showEditableRegion(editor);
|
||||||
}
|
}
|
||||||
// resetting test output
|
// resetting test output
|
||||||
@ -1048,7 +1056,7 @@ const Editor = (props: EditorProps): JSX.Element => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [props.challengeFiles]);
|
}, [props.challengeFiles, props.isResetting]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const { output } = props;
|
const { output } = props;
|
||||||
const editableRegion = getEditableRegionFromRedux();
|
const editableRegion = getEditableRegionFromRedux();
|
||||||
|
@ -36,6 +36,7 @@ export const actionTypes = createTypes(
|
|||||||
'checkChallenge',
|
'checkChallenge',
|
||||||
'executeChallenge',
|
'executeChallenge',
|
||||||
'resetChallenge',
|
'resetChallenge',
|
||||||
|
'stopResetting',
|
||||||
'submitChallenge',
|
'submitChallenge',
|
||||||
|
|
||||||
'moveToTab',
|
'moveToTab',
|
||||||
|
@ -33,6 +33,7 @@ const initialState = {
|
|||||||
hasCompletedBlock: false,
|
hasCompletedBlock: false,
|
||||||
isCodeLocked: false,
|
isCodeLocked: false,
|
||||||
isBuildEnabled: true,
|
isBuildEnabled: true,
|
||||||
|
isResetting: false,
|
||||||
logsOut: [],
|
logsOut: [],
|
||||||
modal: {
|
modal: {
|
||||||
completion: false,
|
completion: false,
|
||||||
@ -117,6 +118,7 @@ export const challengeMounted = createAction(actionTypes.challengeMounted);
|
|||||||
export const checkChallenge = createAction(actionTypes.checkChallenge);
|
export const checkChallenge = createAction(actionTypes.checkChallenge);
|
||||||
export const executeChallenge = createAction(actionTypes.executeChallenge);
|
export const executeChallenge = createAction(actionTypes.executeChallenge);
|
||||||
export const resetChallenge = createAction(actionTypes.resetChallenge);
|
export const resetChallenge = createAction(actionTypes.resetChallenge);
|
||||||
|
export const stopResetting = createAction(actionTypes.stopResetting);
|
||||||
export const submitChallenge = createAction(actionTypes.submitChallenge);
|
export const submitChallenge = createAction(actionTypes.submitChallenge);
|
||||||
|
|
||||||
export const moveToTab = createAction(actionTypes.moveToTab);
|
export const moveToTab = createAction(actionTypes.moveToTab);
|
||||||
@ -146,6 +148,7 @@ export const isCompletionModalOpenSelector = state =>
|
|||||||
export const isHelpModalOpenSelector = state => state[ns].modal.help;
|
export const isHelpModalOpenSelector = state => state[ns].modal.help;
|
||||||
export const isVideoModalOpenSelector = state => state[ns].modal.video;
|
export const isVideoModalOpenSelector = state => state[ns].modal.video;
|
||||||
export const isResetModalOpenSelector = state => state[ns].modal.reset;
|
export const isResetModalOpenSelector = state => state[ns].modal.reset;
|
||||||
|
export const isResettingSelector = state => state[ns].isResetting;
|
||||||
|
|
||||||
export const isBuildEnabledSelector = state => state[ns].isBuildEnabled;
|
export const isBuildEnabledSelector = state => state[ns].isBuildEnabled;
|
||||||
export const successMessageSelector = state => state[ns].successMessage;
|
export const successMessageSelector = state => state[ns].successMessage;
|
||||||
@ -295,9 +298,14 @@ export const reducer = handleActions(
|
|||||||
text,
|
text,
|
||||||
testString
|
testString
|
||||||
})),
|
})),
|
||||||
consoleOut: []
|
consoleOut: [],
|
||||||
|
isResetting: true
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
[actionTypes.stopResetting]: state => ({
|
||||||
|
...state,
|
||||||
|
isResetting: false
|
||||||
|
}),
|
||||||
[actionTypes.updateSolutionFormValues]: (state, { payload }) => ({
|
[actionTypes.updateSolutionFormValues]: (state, { payload }) => ({
|
||||||
...state,
|
...state,
|
||||||
projectFormValues: payload
|
projectFormValues: payload
|
||||||
|
Reference in New Issue
Block a user