import * as React from 'react'; import { useState } from 'react'; import KeymapService, { KeymapItem } from '@joplin/lib/services/KeymapService'; import { ShortcutRecorder } from './ShortcutRecorder'; import getLabel from './utils/getLabel'; import useKeymap from './utils/useKeymap'; import useCommandStatus from './utils/useCommandStatus'; import styles_ from './styles'; import { _ } from '@joplin/lib/locale'; const bridge = require('@electron/remote').require('./bridge').default; import shim from '@joplin/lib/shim'; const keymapService = KeymapService.instance(); export interface KeymapConfigScreenProps { themeId: number; } export const KeymapConfigScreen = ({ themeId }: KeymapConfigScreenProps) => { const styles = styles_(themeId); const [filter, setFilter] = useState(''); const [keymapItems, keymapError, overrideKeymapItems, setAccelerator, resetAccelerator] = useKeymap(); const [recorderError, setRecorderError] = useState(null); const [editing, enableEditing, disableEditing] = useCommandStatus(); const [hovering, enableHovering, disableHovering] = useCommandStatus(); const handleSave = (event: { commandName: string; accelerator: string }) => { const { commandName, accelerator } = event; setAccelerator(commandName, accelerator); disableEditing(commandName); }; const handleReset = (event: { commandName: string }) => { const { commandName } = event; resetAccelerator(commandName); disableEditing(commandName); }; const handleCancel = (event: { commandName: string }) => { const { commandName } = event; disableEditing(commandName); }; const handleError = (event: { recorderError: Error }) => { const { recorderError } = event; setRecorderError(recorderError); }; const handleImport = async () => { const filePath = await bridge().showOpenDialog({ properties: ['openFile'], defaultPath: 'keymap-desktop', filters: [{ name: 'Joplin Keymaps (keymap-desktop.json)', extensions: ['json'] }], }); if (filePath && filePath.length !== 0) { const actualFilePath = filePath[0]; try { const keymapFile = await shim.fsDriver().readFile(actualFilePath, 'utf-8'); overrideKeymapItems(JSON.parse(keymapFile)); } catch (error) { bridge().showErrorMessageBox(_('Error: %s', error.message)); } } }; const handleExport = async () => { const filePath = await bridge().showSaveDialog({ defaultPath: 'keymap-desktop', filters: [{ name: 'Joplin Keymaps (keymap-desktop.json)', extensions: ['json'] }], }); if (filePath) { try { // KeymapService is already synchronized with the in-state keymap await keymapService.saveCustomKeymap(filePath); } catch (error) { bridge().showerrororMessageBox(error.message); } } }; const renderAccelerator = (accelerator: string) => { return (
{accelerator.split('+').map(part => {part}).reduce( (accumulator, part) => (accumulator.length ? [...accumulator, ' + ', part] : [part]), [] )}
); }; const renderStatus = (commandName: string) => { if (editing[commandName]) { return (recorderError && ); } else if (hovering[commandName]) { return (); } else { return null; } }; const renderError = (error: Error) => { return (

{error.message}

); }; const renderKeymapRow = ({ command, accelerator }: KeymapItem) => { const handleClick = () => enableEditing(command); const handleMouseEnter = () => enableHovering(command); const handleMouseLeave = () => disableHovering(command); const cellContent =
{editing[command] ? :
{accelerator ? renderAccelerator(accelerator) :
{_('Disabled')}
}
}
{renderStatus(command)}
; return ( {getLabel(command)} {cellContent} ); }; return (
{keymapError && renderError(keymapError)}
setFilter(event.target.value)} placeholder={_('Search...')} style={styles.filterInput} />
{keymapItems.filter(({ command }) => { const filterLowerCase = filter.toLowerCase(); return (command.toLowerCase().includes(filterLowerCase) || getLabel(command).toLowerCase().includes(filterLowerCase)); }).map(item => renderKeymapRow(item))}
{_('Command')} {_('Keyboard Shortcut')}
); };