diff --git a/src/app/features/call/CallControls.tsx b/src/app/features/call/CallControls.tsx
index 4ffccc7ff..1b9e73f5d 100644
--- a/src/app/features/call/CallControls.tsx
+++ b/src/app/features/call/CallControls.tsx
@@ -83,6 +83,7 @@ export function CallControls({ callEmbed }: CallControlsProps) {
}, [shareConfirm]);
const [pttMode] = useSetting(settingsAtom, 'pttMode');
const [pttKey] = useSetting(settingsAtom, 'pttKey');
+ const [deafenKey] = useSetting(settingsAtom, 'deafenKey');
const [lotusTerminal] = useSetting(settingsAtom, 'lotusTerminal');
const [pttActive, setPttActive] = useState(false);
@@ -209,7 +210,7 @@ export function CallControls({ callEmbed }: CallControlsProps) {
return false;
};
const onKeyDown = (e: KeyboardEvent) => {
- if (e.code !== 'KeyM') return;
+ if (e.code !== deafenKey) return;
if (e.repeat) return;
if (isEditable(e.target as HTMLElement)) return;
e.preventDefault();
@@ -217,7 +218,7 @@ export function CallControls({ callEmbed }: CallControlsProps) {
};
window.addEventListener('keydown', onKeyDown);
return () => window.removeEventListener('keydown', onKeyDown);
- }, [callEmbed]);
+ }, [callEmbed, deafenKey]);
const [hangupState, hangup] = useAsyncCallback(
useCallback(() => callEmbed.hangup(), [callEmbed]),
diff --git a/src/app/features/settings/general/General.tsx b/src/app/features/settings/general/General.tsx
index 3b6ea06eb..32cbe5bcc 100644
--- a/src/app/features/settings/general/General.tsx
+++ b/src/app/features/settings/general/General.tsx
@@ -900,6 +900,37 @@ function Privacy() {
);
}
+function useKeyBind(setter: (code: string) => void) {
+ const [listening, setListening] = useState(false);
+ const listenerRef = useRef<((e: KeyboardEvent) => void) | null>(null);
+
+ useEffect(
+ () => () => {
+ if (listenerRef.current) window.removeEventListener('keydown', listenerRef.current, true);
+ },
+ [],
+ );
+
+ const startListening = useCallback(() => {
+ if (listening) return;
+ setListening(true);
+ const onKey = (e: KeyboardEvent) => {
+ e.preventDefault();
+ if (e.code !== 'Escape') setter(e.code);
+ setListening(false);
+ window.removeEventListener('keydown', onKey, true);
+ listenerRef.current = null;
+ };
+ listenerRef.current = onKey;
+ window.addEventListener('keydown', onKey, true);
+ }, [listening, setter]);
+
+ return { listening, startListening };
+}
+
+const keyLabel = (code: string) =>
+ code === 'Space' ? 'Space' : code.replace('Key', '').replace('Digit', '');
+
function Calls() {
const [cameraOnJoin, setCameraOnJoin] = useSetting(settingsAtom, 'cameraOnJoin');
const [callNoiseSuppression, setCallNoiseSuppression] = useSetting(
@@ -908,37 +939,10 @@ function Calls() {
);
const [pttMode, setPttMode] = useSetting(settingsAtom, 'pttMode');
const [pttKey, setPttKey] = useSetting(settingsAtom, 'pttKey');
- const [listeningForKey, setListeningForKey] = useState(false);
- const keyListenerRef = useRef<((e: KeyboardEvent) => void) | null>(null);
+ const [deafenKey, setDeafenKey] = useSetting(settingsAtom, 'deafenKey');
- useEffect(
- () => () => {
- if (keyListenerRef.current)
- window.removeEventListener('keydown', keyListenerRef.current, true);
- },
- [],
- );
-
- const handleKeyBind = useCallback(() => {
- if (listeningForKey) return;
- setListeningForKey(true);
- const onKey = (e: KeyboardEvent) => {
- e.preventDefault();
- if (e.code === 'Escape') {
- setListeningForKey(false);
- } else {
- setPttKey(e.code);
- setListeningForKey(false);
- }
- window.removeEventListener('keydown', onKey, true);
- keyListenerRef.current = null;
- };
- keyListenerRef.current = onKey;
- window.addEventListener('keydown', onKey, true);
- }, [listeningForKey, setPttKey]);
-
- const keyLabel = (code: string) =>
- code === 'Space' ? 'Space' : code.replace('Key', '').replace('Digit', '');
+ const pttBind = useKeyBind(setPttKey);
+ const deafenBind = useKeyBind(setDeafenKey);
return (
@@ -976,23 +980,40 @@ function Calls() {
/>
{pttMode && (
- {listeningForKey ? 'Press a key…' : keyLabel(pttKey)}
+ {pttBind.listening ? 'Press a key…' : keyLabel(pttKey)}
}
/>
)}
+
+ {deafenBind.listening ? 'Press a key…' : keyLabel(deafenKey)}
+
+ }
+ />
);
@@ -1295,6 +1316,43 @@ function Messages() {
);
}
+function KeyboardShortcuts() {
+ const [quickSwitcherKey, setQuickSwitcherKey] = useSetting(settingsAtom, 'quickSwitcherKey');
+ const qsBind = useKeyBind(setQuickSwitcherKey);
+
+ return (
+
+ Keyboard Shortcuts
+
+
+
+ {qsBind.listening ? 'Press a key…' : `Ctrl+${keyLabel(quickSwitcherKey)}`}
+
+
+ }
+ />
+
+
+ );
+}
+
type GeneralProps = {
requestClose: () => void;
};
@@ -1325,6 +1383,7 @@ export function General({ requestClose }: GeneralProps) {
+
diff --git a/src/app/pages/client/ClientNonUIFeatures.tsx b/src/app/pages/client/ClientNonUIFeatures.tsx
index b398ce406..c8757ac1d 100644
--- a/src/app/pages/client/ClientNonUIFeatures.tsx
+++ b/src/app/pages/client/ClientNonUIFeatures.tsx
@@ -264,19 +264,20 @@ function MessageNotifications() {
function QuickSwitcherFeature() {
const [open, setOpen] = useState(false);
+ const [quickSwitcherKey] = useSetting(settingsAtom, 'quickSwitcherKey');
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
- if ((e.ctrlKey || e.metaKey) && e.key === 'p') {
+ if ((e.ctrlKey || e.metaKey) && e.code === quickSwitcherKey) {
e.preventDefault();
- setOpen(true);
+ setOpen((prev) => !prev);
}
};
window.addEventListener('keydown', handleKeyDown);
return () => {
window.removeEventListener('keydown', handleKeyDown);
};
- }, []);
+ }, [quickSwitcherKey]);
if (!open) return null;
return setOpen(false)} />;
diff --git a/src/app/state/settings.ts b/src/app/state/settings.ts
index 1f09a1a8c..f40a73b0a 100644
--- a/src/app/state/settings.ts
+++ b/src/app/state/settings.ts
@@ -80,6 +80,9 @@ export interface Settings {
nightLightEnabled: boolean;
nightLightOpacity: number;
+
+ deafenKey: string;
+ quickSwitcherKey: string;
}
const defaultSettings: Settings = {
@@ -129,6 +132,9 @@ const defaultSettings: Settings = {
nightLightEnabled: false,
nightLightOpacity: 30,
+
+ deafenKey: 'KeyM',
+ quickSwitcherKey: 'KeyP',
};
export const getSettings = (): Settings => {