diff --git a/client/src/templates/Challenges/classic/Editor.js b/client/src/templates/Challenges/classic/Editor.js index 2fc9965726..298de90b57 100644 --- a/client/src/templates/Challenges/classic/Editor.js +++ b/client/src/templates/Challenges/classic/Editor.js @@ -1,10 +1,14 @@ import React, { Component, Suspense } from 'react'; import PropTypes from 'prop-types'; -import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; -import { executeChallenge, updateFile } from '../redux'; +import { + canFocusEditorSelector, + executeChallenge, + setEditorFocusability, + updateFile +} from '../redux'; import { userSelector, isDonationModalOpenSelector } from '../../../redux'; import { Loader } from '../../../components/helpers'; @@ -18,27 +22,26 @@ const propTypes = { executeChallenge: PropTypes.func.isRequired, ext: PropTypes.string, fileKey: PropTypes.string, + setEditorFocusability: PropTypes.func, theme: PropTypes.string, updateFile: PropTypes.func.isRequired }; const mapStateToProps = createSelector( + canFocusEditorSelector, isDonationModalOpenSelector, userSelector, - (open, { theme = 'night' }) => ({ - canFocus: !open, + (canFocus, open, { theme = 'night' }) => ({ + canFocus: open ? false : canFocus, theme }) ); -const mapDispatchToProps = dispatch => - bindActionCreators( - { - executeChallenge, - updateFile - }, - dispatch - ); +const mapDispatchToProps = { + setEditorFocusability, + executeChallenge, + updateFile +}; const modeMap = { css: 'css', @@ -110,13 +113,15 @@ class Editor extends Component { editorDidMount = (editor, monaco) => { this._editor = editor; - if (this.props.canFocus) this._editor.focus(); + if (this.props.canFocus) { + this._editor.focus(); + } else this.focusOnHotkeys(); this._editor.addAction({ id: 'execute-challenge', label: 'Run tests', keybindings: [ /* eslint-disable no-bitwise */ - monaco.KeyMod.chord(monaco.KeyMod.WinCtrl | monaco.KeyCode.Enter) + monaco.KeyMod.chord(monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter) ], run: this.props.executeChallenge }); @@ -125,12 +130,21 @@ class Editor extends Component { label: 'Leave editor', keybindings: [monaco.KeyCode.Escape], run: () => { - if (this.props.containerRef.current) - this.props.containerRef.current.focus(); + this.focusOnHotkeys(); + this.props.setEditorFocusability(false); } }); + this._editor.onDidFocusEditorWidget(() => + this.props.setEditorFocusability(true) + ); }; + focusOnHotkeys() { + if (this.props.containerRef.current) { + this.props.containerRef.current.focus(); + } + } + onChange = editorValue => { const { updateFile, fileKey } = this.props; updateFile({ key: fileKey, editorValue }); diff --git a/client/src/templates/Challenges/components/Hotkeys.js b/client/src/templates/Challenges/components/Hotkeys.js index e38902f14e..9a17a267af 100644 --- a/client/src/templates/Challenges/components/Hotkeys.js +++ b/client/src/templates/Challenges/components/Hotkeys.js @@ -4,9 +4,9 @@ import { HotKeys, GlobalHotKeys } from 'react-hotkeys'; import { navigate } from 'gatsby'; const keyMap = { - EXECUTE_CHALLENGE: ['ctrl+enter'], - NAVIGATE_PREV: ['ctrl+left'], - NAVIGATE_NEXT: ['ctrl+right'] + EXECUTE_CHALLENGE: ['ctrl+enter', 'command+enter'], + NAVIGATE_PREV: ['p'], + NAVIGATE_NEXT: ['n'] }; const propTypes = { diff --git a/client/src/templates/Challenges/redux/index.js b/client/src/templates/Challenges/redux/index.js index 6076fcd4a4..a8c7a8435a 100644 --- a/client/src/templates/Challenges/redux/index.js +++ b/client/src/templates/Challenges/redux/index.js @@ -18,6 +18,7 @@ export const ns = 'challenge'; export const backendNS = 'backendChallenge'; const initialState = { + canFocusEditor: true, challengeFiles: {}, challengeMeta: { id: '', @@ -75,7 +76,9 @@ export const types = createTypes( 'resetChallenge', 'submitChallenge', - 'moveToTab' + 'moveToTab', + + 'setEditorFocusability' ], ns ); @@ -148,6 +151,8 @@ export const submitChallenge = createAction(types.submitChallenge); export const moveToTab = createAction(types.moveToTab); +export const setEditorFocusability = createAction(types.setEditorFocusability); + export const currentTabSelector = state => state[ns].currentTab; export const challengeFilesSelector = state => state[ns].challengeFiles; export const challengeMetaSelector = state => state[ns].challengeMeta; @@ -217,6 +222,8 @@ export const challengeDataSelector = state => { return challengeData; }; +export const canFocusEditorSelector = state => state[ns].canFocusEditor; + const MAX_LOGS_SIZE = 64 * 1024; export const reducer = handleActions( @@ -348,6 +355,10 @@ export const reducer = handleActions( [types.executeChallenge]: state => ({ ...state, currentTab: 3 + }), + [types.setEditorFocusability]: (state, { payload }) => ({ + ...state, + canFocusEditor: payload }) }, initialState