feat: Code saveing to localStorage on Cmd/Ctrl + S (#38324)

Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
Co-authored-by: mrugesh <1884376+raisedadead@users.noreply.github.com>
This commit is contained in:
Jacob Robinson
2020-03-13 06:20:14 -07:00
committed by GitHub
parent 814256ca56
commit 694e52f742
3 changed files with 44 additions and 7 deletions

View File

@@ -7,6 +7,7 @@ import {
canFocusEditorSelector,
executeChallenge,
inAccessibilityModeSelector,
saveEditorContent,
setEditorFocusability,
setAccessibilityMode,
updateFile
@@ -25,6 +26,7 @@ const propTypes = {
ext: PropTypes.string,
fileKey: PropTypes.string,
inAccessibilityMode: PropTypes.bool.isRequired,
saveEditorContent: PropTypes.func.isRequired,
setAccessibilityMode: PropTypes.func.isRequired,
setEditorFocusability: PropTypes.func,
theme: PropTypes.string,
@@ -44,9 +46,10 @@ const mapStateToProps = createSelector(
);
const mapDispatchToProps = {
setEditorFocusability,
setAccessibilityMode,
executeChallenge,
saveEditorContent,
setAccessibilityMode,
setEditorFocusability,
updateFile
};
@@ -150,6 +153,14 @@ class Editor extends Component {
this.props.setEditorFocusability(false);
}
});
this._editor.addAction({
id: 'save-editor-content',
label: 'Save editor content to localStorage',
keybindings: [
monaco.KeyMod.chord(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_S)
],
run: this.props.saveEditorContent
});
this._editor.addAction({
id: 'toggle-accessibility',
label: 'Toggle Accessibility Mode',

View File

@@ -1,5 +1,5 @@
import { of } from 'rxjs';
import { filter, switchMap, tap, ignoreElements } from 'rxjs/operators';
import { filter, switchMap, map, tap, ignoreElements } from 'rxjs/operators';
import { combineEpics, ofType } from 'redux-observable';
import store from 'store';
@@ -16,6 +16,8 @@ import { types as appTypes } from '../../../redux';
import { setContent, isPoly } from '../utils/polyvinyl';
import { createFlashMessage } from '../../../components/Flash/redux';
const legacyPrefixes = [
'Bonfire: ',
'Waypoint: ',
@@ -73,16 +75,38 @@ function clearCodeEpic(action$, state$) {
function saveCodeEpic(action$, state$) {
return action$.pipe(
ofType(types.executeChallenge),
ofType(types.executeChallenge, types.saveEditorContent),
// do not save challenge if code is locked
filter(() => !isCodeLockedSelector(state$.value)),
tap(() => {
map(action => {
const state = state$.value;
const { id } = challengeMetaSelector(state);
const files = challengeFilesSelector(state);
store.set(id, files);
try {
store.set(id, files);
// Possible fileType values: indexhtml indexjs indexjsx
// The files Object always has one of these as the first/only attribute
const fileType = Object.keys(files)[0];
if (store.get(id)[fileType].contents !== files[fileType].contents) {
throw Error('Failed to save to localStorage');
}
return action;
} catch (e) {
return { ...action, error: true };
}
}),
ignoreElements()
ofType(types.saveEditorContent),
switchMap(({ error }) =>
of(
createFlashMessage({
type: error ? 'warning' : 'success',
message: error
? // eslint-disable-next-line max-len
"Oops, your code did not save, your browser's local storage may be full."
: "Saved! Your code was saved to your browser's local storage."
})
)
)
);
}

View File

@@ -70,6 +70,7 @@ export const types = createTypes(
'disableBuildOnError',
'storedCodeFound',
'noStoredCodeFound',
'saveEditorContent',
'closeModal',
'openModal',
@@ -147,6 +148,7 @@ export const unlockCode = createAction(types.unlockCode);
export const disableBuildOnError = createAction(types.disableBuildOnError);
export const storedCodeFound = createAction(types.storedCodeFound);
export const noStoredCodeFound = createAction(types.noStoredCodeFound);
export const saveEditorContent = createAction(types.saveEditorContent);
export const closeModal = createAction(types.closeModal);
export const openModal = createAction(types.openModal);