summaryrefslogtreecommitdiff
path: root/apps/web/src/hooks/use-keybindings.ts
blob: e59d1eaf0ae4fd7b523a996257e62d031899ee5b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import { useEffect } from "react";
import { invokeAction } from "@/lib/actions";
import { useKeybindingsStore } from "@/stores/keybindings-store";

/**
 * a composable that hooks to the caller component's
 * lifecycle and hooks to the keyboard events to fire
 * the appropriate actions based on keybindings
 */
export function useKeybindingsListener() {
	const { keybindings, getKeybindingString, keybindingsEnabled, isRecording } =
		useKeybindingsStore();

	useEffect(() => {
		const eventOptions: AddEventListenerOptions = { capture: true };
		const handleKeyDown = (ev: KeyboardEvent) => {
			// do not check keybinds if the mode is disabled
			if (!keybindingsEnabled) return;
			// ignore key events if user is changing keybindings
			if (isRecording) return;

			const binding = getKeybindingString(ev);
			if (!binding) return;

			const boundAction = keybindings[binding];
			if (!boundAction) return;

			const activeElement = document.activeElement;
			const isTextInput =
				activeElement &&
				(activeElement.tagName === "INPUT" ||
					activeElement.tagName === "TEXTAREA" ||
					(activeElement as HTMLElement).isContentEditable);

			if (isTextInput) return;

			ev.preventDefault();

			switch (boundAction) {
				case "seek-forward":
					invokeAction("seek-forward", { seconds: 1 }, "keypress");
					break;
				case "seek-backward":
					invokeAction("seek-backward", { seconds: 1 }, "keypress");
					break;
				case "jump-forward":
					invokeAction("jump-forward", { seconds: 5 }, "keypress");
					break;
				case "jump-backward":
					invokeAction("jump-backward", { seconds: 5 }, "keypress");
					break;
				default:
					invokeAction(boundAction, undefined, "keypress");
			}
		};

		document.addEventListener("keydown", handleKeyDown, eventOptions);

		return () => {
			document.removeEventListener("keydown", handleKeyDown, eventOptions);
		};
	}, [keybindings, getKeybindingString, keybindingsEnabled, isRecording]);
}

/**
 * this composable allows for the UI component to be disabled if the component in question is mounted
 */
export function useKeybindingDisabler() {
	const { disableKeybindings, enableKeybindings } = useKeybindingsStore();

	return {
		disableKeybindings,
		enableKeybindings,
	};
}