fix: make crtl+s save to appropriate location (#45406)

* fix: crtl+s for multifile cert projects

* fix: remove unused code

* feat: add limit on save frequency
This commit is contained in:
Tom
2022-04-04 07:32:43 -05:00
committed by GitHub
parent f43de0969e
commit 3d242179b2
5 changed files with 36 additions and 4 deletions

View File

@ -533,6 +533,7 @@
"local-code-saved": "Saved! Your code was saved to your browser's local storage.", "local-code-saved": "Saved! Your code was saved to your browser's local storage.",
"code-saved": "Your code was saved to the database. It will be here when you return.", "code-saved": "Your code was saved to the database. It will be here when you return.",
"code-save-error": "An error occurred trying to save your code.", "code-save-error": "An error occurred trying to save your code.",
"code-save-less": "Slow Down! Your code was not saved. Try again in a few seconds.",
"challenge-save-too-big": "Sorry, you cannot save your code. Your code is {{user-size}} bytes. We allow a maximum of {{max-size}} bytes. Please make your code smaller and try again or request assistance on https://forum.freecodecamp.org", "challenge-save-too-big": "Sorry, you cannot save your code. Your code is {{user-size}} bytes. We allow a maximum of {{max-size}} bytes. Please make your code smaller and try again or request assistance on https://forum.freecodecamp.org",
"challenge-submit-too-big": "Sorry, you cannot submit your code. Your code is {{user-size}} bytes. We allow a maximum of {{max-size}} bytes. Please make your code smaller and try again or request assistance on https://forum.freecodecamp.org" "challenge-submit-too-big": "Sorry, you cannot submit your code. Your code is {{user-size}} bytes. We allow a maximum of {{max-size}} bytes. Please make your code smaller and try again or request assistance on https://forum.freecodecamp.org"
}, },

View File

@ -9,6 +9,7 @@ export enum FlashMessages {
ChallengeSubmitTooBig = 'flash.challenge-submit-too-big', ChallengeSubmitTooBig = 'flash.challenge-submit-too-big',
CodeSaved = 'flash.code-saved', CodeSaved = 'flash.code-saved',
CodeSaveError = 'flash.code-save-error', CodeSaveError = 'flash.code-save-error',
CodeSaveLess = 'flash.code-save-less',
CompleteProjectFirst = 'flash.complete-project-first', CompleteProjectFirst = 'flash.complete-project-first',
DeleteTokenErr = 'flash.delete-token-err', DeleteTokenErr = 'flash.delete-token-err',
EmailValid = 'flash.email-valid', EmailValid = 'flash.email-valid',

View File

@ -13,11 +13,23 @@ import {
bodySizeFits, bodySizeFits,
MAX_BODY_SIZE MAX_BODY_SIZE
} from '../utils/challenge-request-helpers'; } from '../utils/challenge-request-helpers';
import { saveChallengeComplete } from './'; import { saveChallengeComplete, savedChallengesSelector } from './';
export function* saveChallengeSaga() { export function* saveChallengeSaga() {
const { id, challengeType } = yield select(challengeMetaSelector); const { id, challengeType } = yield select(challengeMetaSelector);
const { challengeFiles } = yield select(challengeDataSelector); const { challengeFiles } = yield select(challengeDataSelector);
const savedChallenges = yield select(savedChallengesSelector);
const savedChallenge = savedChallenges.find(challenge => challenge.id === id);
// don't let users save more than once every 5 seconds
if (Date.now() - savedChallenge?.lastSavedDate < 5000) {
return yield put(
createFlashMessage({
type: 'danger',
message: FlashMessages.CodeSaveLess
})
);
}
// only allow saving of multiFileCertProject's // only allow saving of multiFileCertProject's
if (challengeType === challengeTypes.multiFileCertProject) { if (challengeType === challengeTypes.multiFileCertProject) {

View File

@ -20,7 +20,11 @@ import store from 'store';
import { Loader } from '../../../components/helpers'; import { Loader } from '../../../components/helpers';
import { Themes } from '../../../components/settings/theme'; import { Themes } from '../../../components/settings/theme';
import { userSelector, isDonationModalOpenSelector } from '../../../redux'; import {
userSelector,
saveChallenge,
isDonationModalOpenSelector
} from '../../../redux';
import { import {
ChallengeFiles, ChallengeFiles,
Dimensions, Dimensions,
@ -31,8 +35,10 @@ import {
} from '../../../redux/prop-types'; } from '../../../redux/prop-types';
import { editorToneOptions } from '../../../utils/tone/editor-config'; import { editorToneOptions } from '../../../utils/tone/editor-config';
import { editorNotes } from '../../../utils/tone/editor-notes'; import { editorNotes } from '../../../utils/tone/editor-notes';
import { challengeTypes } from '../../../../utils/challenge-types';
import { import {
canFocusEditorSelector, canFocusEditorSelector,
challengeMetaSelector,
consoleOutputSelector, consoleOutputSelector,
executeChallenge, executeChallenge,
saveEditorContent, saveEditorContent,
@ -54,6 +60,7 @@ const MonacoEditor = Loadable(() => import('react-monaco-editor'));
interface EditorProps { interface EditorProps {
canFocus: boolean; canFocus: boolean;
challengeFiles: ChallengeFiles; challengeFiles: ChallengeFiles;
challengeType: number;
containerRef: RefObject<HTMLElement>; containerRef: RefObject<HTMLElement>;
contents: string; contents: string;
description: string; description: string;
@ -70,6 +77,7 @@ interface EditorProps {
isResetting: boolean; isResetting: boolean;
output: string[]; output: string[];
resizeProps: ResizeProps; resizeProps: ResizeProps;
saveChallenge: () => void;
saveEditorContent: () => void; saveEditorContent: () => void;
setEditorFocusability: (isFocusable: boolean) => void; setEditorFocusability: (isFocusable: boolean) => void;
submitChallenge: () => void; submitChallenge: () => void;
@ -105,6 +113,7 @@ interface EditorProperties {
const mapStateToProps = createSelector( const mapStateToProps = createSelector(
canFocusEditorSelector, canFocusEditorSelector,
challengeMetaSelector,
consoleOutputSelector, consoleOutputSelector,
isDonationModalOpenSelector, isDonationModalOpenSelector,
isProjectPreviewModalOpenSelector, isProjectPreviewModalOpenSelector,
@ -113,6 +122,7 @@ const mapStateToProps = createSelector(
challengeTestsSelector, challengeTestsSelector,
( (
canFocus: boolean, canFocus: boolean,
{ challengeType }: { challengeType: number },
output: string[], output: string[],
open, open,
previewOpen: boolean, previewOpen: boolean,
@ -121,6 +131,7 @@ const mapStateToProps = createSelector(
tests: [{ text: string; testString: string }] tests: [{ text: string; testString: string }]
) => ({ ) => ({
canFocus: open ? false : canFocus, canFocus: open ? false : canFocus,
challengeType,
previewOpen, previewOpen,
isResetting, isResetting,
output, output,
@ -133,6 +144,7 @@ const mapStateToProps = createSelector(
const mapDispatchToProps = { const mapDispatchToProps = {
executeChallenge, executeChallenge,
saveChallenge,
saveEditorContent, saveEditorContent,
setEditorFocusability, setEditorFocusability,
updateFile, updateFile,
@ -396,9 +408,14 @@ const Editor = (props: EditorProps): JSX.Element => {
}); });
editor.addAction({ editor.addAction({
id: 'save-editor-content', id: 'save-editor-content',
label: 'Save editor content to localStorage', label: 'Save editor content',
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_S], keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_S],
run: props.saveEditorContent run:
props.challengeType === challengeTypes.multiFileCertProject
? // save to database
props.saveChallenge
: // save to local storage
props.saveEditorContent
}); });
editor.addAction({ editor.addAction({
id: 'toggle-accessibility', id: 'toggle-accessibility',

View File

@ -24,6 +24,7 @@ const toneUrls = {
[FlashMessages.ChallengeSubmitTooBig]: TRY_AGAIN, [FlashMessages.ChallengeSubmitTooBig]: TRY_AGAIN,
[FlashMessages.CodeSaved]: CHAL_COMP, [FlashMessages.CodeSaved]: CHAL_COMP,
[FlashMessages.CodeSaveError]: TRY_AGAIN, [FlashMessages.CodeSaveError]: TRY_AGAIN,
[FlashMessages.CodeSaveLess]: TRY_AGAIN,
[FlashMessages.CompleteProjectFirst]: TRY_AGAIN, [FlashMessages.CompleteProjectFirst]: TRY_AGAIN,
[FlashMessages.DeleteTokenErr]: TRY_AGAIN, [FlashMessages.DeleteTokenErr]: TRY_AGAIN,
[FlashMessages.EmailValid]: CHAL_COMP, [FlashMessages.EmailValid]: CHAL_COMP,