diff --git a/src/actions/settings.ts b/src/actions/settings.ts index e2f477d9..eb624360 100644 --- a/src/actions/settings.ts +++ b/src/actions/settings.ts @@ -7,6 +7,7 @@ import { SettingId } from '../settings/user'; /** Actions related to settings. */ export enum SettingsActionType { SetBoolean = 'settings.action.setBoolean', + ToggleBoolean = 'settings.action.toggleBoolean', DidFailToSetBoolean = 'settings.action.didFailToSetBoolean', DidBooleanChange = 'settings.action.didBooleanChange', } @@ -27,6 +28,16 @@ export function setBoolean(id: SettingId, newState: boolean): SettingsSetBoolean return { type: SettingsActionType.SetBoolean, id, newState }; } +/** Action to toggle a setting. */ +export type SettingsToggleBooleanAction = Action & { + id: SettingId; +}; + +/** Creates an action to toggle a setting. */ +export function toggleBoolean(id: SettingId): SettingsToggleBooleanAction { + return { type: SettingsActionType.ToggleBoolean, id }; +} + /** Action that indicates setting/storing a setting failed. */ export type SettingsDidFailToSetBooleanAction = Action & { id: SettingId; @@ -56,5 +67,6 @@ export function didBooleanChange( /** Common type for all settings actions. */ export type SettingsAction = | SettingsSetBooleanAction + | SettingsToggleBooleanAction | SettingsDidFailToSetBooleanAction | SettingsDidBooleanChangeAction; diff --git a/src/components/Editor.tsx b/src/components/Editor.tsx index c6588c7d..222b51f0 100644 --- a/src/components/Editor.tsx +++ b/src/components/Editor.tsx @@ -17,7 +17,9 @@ import { connect } from 'react-redux'; import { Action, Dispatch } from '../actions'; import { setEditSession, storageChanged } from '../actions/editor'; import { compile } from '../actions/mpy'; +import { toggleBoolean } from '../actions/settings'; import { RootState } from '../reducers'; +import { SettingId } from '../settings/user'; import { isMacOS } from '../utils/os'; import { EditorStringId } from './editor-i18n'; import en from './editor-i18n.en.json'; @@ -34,12 +36,14 @@ import './editor.scss'; type StateProps = { darkMode: boolean; + showDocs: boolean; }; type DispatchProps = { onSessionChanged: (session?: Ace.EditSession) => void; onProgramStorageChanged: (newValue: string) => void; onCheck: (script: string) => void; + onToggleDocs: () => void; }; type EditorProps = StateProps & DispatchProps & WithI18nProps; @@ -78,7 +82,7 @@ class Editor extends React.Component { } render(): JSX.Element { - const { darkMode, i18n, onSessionChanged, onCheck } = this.props; + const { i18n, darkMode, onSessionChanged, onCheck, onToggleDocs } = this.props; return (
this.editor?.resize()}> @@ -105,6 +109,13 @@ class Editor extends React.Component { mac: 'Shift-F2', }; + // we want to use Ctrl-D for docs toggle, so change + // delete line to VSCode default + e.commands.byName['removeline'].bindKey = { + win: 'Ctrl-Shift-K', + mac: 'Cmd-Shift-K', + }; + config.loadModule( 'ace/ext/menu_tools/get_editor_keyboard_shortcuts', (m) => { @@ -129,6 +140,11 @@ class Editor extends React.Component { bindKey: { win: 'F2', mac: 'F2' }, exec: (editor) => onCheck(editor.getValue()), }, + { + name: 'toggleDocs', + bindKey: { win: 'Ctrl-D', mac: 'Cmd-D' }, + exec: () => onToggleDocs(), + }, { name: 'save', bindKey: { win: 'Ctrl-S', mac: 'Cmd-S' }, @@ -202,6 +218,7 @@ class Editor extends React.Component { const mapStateToProps = (state: RootState): StateProps => ({ darkMode: state.settings.darkMode, + showDocs: state.settings.showDocs, }); const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({ @@ -210,6 +227,7 @@ const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({ // REVISIT: the options here might need to be changed - hopefully there is // one setting that works for all hub types for cases where we aren't connected. onCheck: (script) => dispatch(compile(script)), + onToggleDocs: () => dispatch(toggleBoolean(SettingId.ShowDocs)), }); export default connect( diff --git a/src/sagas/settings.test.ts b/src/sagas/settings.test.ts index da9dea7c..00c1e623 100644 --- a/src/sagas/settings.test.ts +++ b/src/sagas/settings.test.ts @@ -5,7 +5,12 @@ import { AsyncSaga } from '../../test'; import { didStart } from '../actions/app'; -import { didBooleanChange, didFailToSetBoolean, setBoolean } from '../actions/settings'; +import { + didBooleanChange, + didFailToSetBoolean, + setBoolean, + toggleBoolean, +} from '../actions/settings'; import { SettingId } from '../settings/user'; import settings from './settings'; @@ -345,3 +350,24 @@ describe('storage monitor', () => { await saga.end(); }); }); + +describe('toggle', () => { + test('showDocs', async () => { + const saga = new AsyncSaga(settings, { settings: { showDocs: false } }); + + const mockSetItem = jest + .spyOn(Object.getPrototypeOf(window.localStorage), 'setItem') + .mockImplementation((key, value) => { + expect(key).toBe('setting.showDocs'); + expect(value).toBe('true'); + }); + + saga.put(toggleBoolean(SettingId.ShowDocs)); + expect(mockSetItem).toHaveBeenCalled(); + + const action = await saga.take(); + expect(action).toEqual(didBooleanChange(SettingId.ShowDocs, true)); + + await saga.end(); + }); +}); diff --git a/src/sagas/settings.ts b/src/sagas/settings.ts index 3bd84274..394c9475 100644 --- a/src/sagas/settings.ts +++ b/src/sagas/settings.ts @@ -11,8 +11,10 @@ import { AppActionType } from '../actions/app'; import { SettingsActionType, SettingsSetBooleanAction, + SettingsToggleBooleanAction, didBooleanChange, didFailToSetBoolean, + setBoolean, } from '../actions/settings'; import { RootState } from '../reducers'; import { SettingId, getDefaultBooleanValue } from '../settings/user'; @@ -97,8 +99,14 @@ function* storeSetting(action: SettingsSetBooleanAction): Generator { } } +function* toggleSetting(action: SettingsToggleBooleanAction): Generator { + const oldValue = yield* select((s: RootState) => s.settings[action.id]); + yield* storeSetting(setBoolean(action.id, !oldValue)); +} + export default function* (): Generator { yield* fork(monitorLocalStorage); yield* takeEvery(AppActionType.DidStart, loadSettings); yield* takeEvery(SettingsActionType.SetBoolean, storeSetting); + yield* takeEvery(SettingsActionType.ToggleBoolean, toggleSetting); }