settings: customize shortcuts

This commit is contained in:
Liam Fitzgerald 2021-05-05 14:27:51 +10:00
parent 880cde81ca
commit 6d403b67fd
No known key found for this signature in database
GPG Key ID: D390E12C61D1CFFB
4 changed files with 140 additions and 7 deletions

View File

@ -47,6 +47,17 @@ export function parentPath(path: string) {
return _.dropRight(path.split('/'), 1).join('/');
}
export const getChord = (e: KeyboardEvent) => {
let chord = [e.key];
if(e.metaKey) {
chord.unshift('meta');
}
if(e.ctrlKey) {
chord.unshift('ctrl');
}
return chord.join('+');
}
export function getResourcePath(workspace: Workspace, path: string, joined: boolean, mod: string) {
const base = workspace.type === 'group'
? `/~landscape${workspace.group}`

View File

@ -2,6 +2,14 @@ import f from 'lodash/fp';
import { RemoteContentPolicy, LeapCategories, leapCategories } from "~/types/local-update";
import { BaseState, createState } from '~/logic/state/base';
export interface ShortcutMapping {
cycleForward: string;
cycleBack: string;
navForward: string;
navBack: string;
hideSidebar: string;
}
export interface SettingsState extends BaseState<SettingsState> {
display: {
@ -17,13 +25,7 @@ export interface SettingsState extends BaseState<SettingsState> {
hideGroups: boolean;
hideUtilities: boolean;
};
keyboard: {
cycleForward: string;
cycleBack: string;
navForward: string;
navBack: string;
hideSidebar: string;
}
keyboard: ShortcutMapping;
remoteContentPolicy: RemoteContentPolicy;
leap: {
categories: LeapCategories[];

View File

@ -0,0 +1,117 @@
import React, { useCallback, useEffect, useState } from 'react';
import _ from 'lodash';
import { Box, Col, Text } from '@tlon/indigo-react';
import { Formik, Form, useField } from 'formik';
import GlobalApi from '~/logic/api/global';
import { getChord } from '~/logic/lib/util';
import useSettingsState, {
selectSettingsState,
ShortcutMapping,
} from '~/logic/state/settings';
import { AsyncButton } from '~/views/components/AsyncButton';
import { BackButton } from './BackButton';
interface ShortcutSettingsProps {
api: GlobalApi;
}
const settingsSel = selectSettingsState(['keyboard']);
export function ChordInput(props: { id: string; label: string }) {
const { id, label } = props;
const [capturing, setCapturing] = useState(false);
const [{ value }, , { setValue }] = useField(id);
const onCapture = useCallback(() => {
setCapturing(true);
}, []);
useEffect(() => {
if (!capturing) {
return;
}
function onKeydown(e: KeyboardEvent) {
if (['Control', 'Shift', 'Meta'].includes(e.key)) {
return;
}
const chord = getChord(e);
setValue(chord);
e.stopImmediatePropagation();
e.preventDefault();
setCapturing(false);
}
document.addEventListener('keydown', onKeydown);
return () => {
document.removeEventListener('keydown', onKeydown);
};
}, [capturing]);
return (
<>
<Box p="1">
<Text>{label}</Text>
</Box>
<Box
border="1"
borderColor="lightGray"
borderRadius="2"
onClick={onCapture}
p="1"
>
<Text>{capturing ? 'Press' : value}</Text>
</Box>
</>
);
}
export default function ShortcutSettings(props: ShortcutSettingsProps) {
const { api } = props;
const { keyboard } = useSettingsState(settingsSel);
return (
<Formik
initialValues={keyboard}
onSubmit={async (values: ShortcutMapping, actions) => {
const promises = _.map(values, (value, key) => {
return keyboard[key] !== value
? api.settings.putEntry('keyboard', key, value)
: Promise.resolve();
});
await Promise.all(promises);
actions.setStatus({ success: null });
}}
>
<Form>
<BackButton />
<Col p="5" pt="4" gapY="5">
<Col gapY="1" mt="0">
<Text color="black" fontSize={2} fontWeight="medium">
Shortcuts
</Text>
<Text gray>Customize keyboard shortcuts for landscape</Text>
</Col>
<Box
display="grid"
gridTemplateColumns="1fr 100px"
gridGap={3}
maxWidth="500px"
>
<ChordInput id="navForward" label="Go forward in history" />
<ChordInput id="navBack" label="Go backward in history" />
<ChordInput
id="cycleForward"
label="Cycle forward through channel list"
/>
<ChordInput
id="cycleBack"
label="Cycle backward through channel list"
/>
<ChordInput id="hideSidebar" label="Show/hide group sidebar" />
</Box>
<AsyncButton primary width="fit-content">Save Changes</AsyncButton>
</Col>
</Form>
</Formik>
);
}

View File

@ -15,6 +15,7 @@ import { SidebarItem as BaseSidebarItem } from '~/views/landscape/components/Sid
import { PropFunc } from '~/types';
import DebugPane from './components/lib/Debug';
import useHarkState from '~/logic/state/hark';
import ShortcutSettings from './components/lib/ShortcutSettings';
export const Skeleton = (props: { children: ReactNode }) => (
<Box height='100%' width='100%' px={[0, 3]} pb={[0, 3]} borderRadius={1}>
@ -115,6 +116,7 @@ export default function SettingsScreen(props: any) {
<SidebarItem icon='Upload' text='Remote Storage' hash='s3' />
<SidebarItem icon='LeapArrow' text='Leap' hash='leap' />
<SidebarItem icon='Node' text='CalmEngine' hash='calm' />
<SidebarItem icon='Keyboard' text='Shortcuts' hash='shortcuts' />
<SidebarItem
icon='Locked'
text='Devices + Security'
@ -131,6 +133,7 @@ export default function SettingsScreen(props: any) {
/>
)}
{hash === 'display' && <DisplayForm api={props.api} />}
{hash === 'shortcuts' && <ShortcutSettings api={props.api} />}
{hash === 's3' && <S3Form api={props.api} />}
{hash === 'leap' && <LeapSettings api={props.api} />}
{hash === 'calm' && <CalmPrefs api={props.api} />}