Merge pull request #1015 from robintown/shortcut-focus

Make keyboard shortcuts accessible by default
This commit is contained in:
Robin 2023-04-20 10:45:11 -04:00 committed by GitHub
commit 4719a92ffc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 30 additions and 48 deletions

View file

@ -106,7 +106,6 @@
"Show call inspector": "Show call inspector",
"Sign in": "Sign in",
"Sign out": "Sign out",
"Single-key keyboard shortcuts": "Single-key keyboard shortcuts",
"Spatial audio": "Spatial audio",
"Speaker": "Speaker",
"Speaker {{n}}": "Speaker {{n}}",
@ -138,7 +137,6 @@
"Walkie-talkie call": "Walkie-talkie call",
"Walkie-talkie call name": "Walkie-talkie call name",
"WebRTC is not supported or is being blocked in this browser.": "WebRTC is not supported or is being blocked in this browser.",
"Whether to enable single-key keyboard shortcuts, e.g. 'm' to mute/unmute the mic.": "Whether to enable single-key keyboard shortcuts, e.g. 'm' to mute/unmute the mic.",
"Yes, join call": "Yes, join call",
"You can't talk at the same time": "You can't talk at the same time",
"Your recent calls": "Your recent calls"

View file

@ -157,7 +157,7 @@ export function InCallView({
const { hideScreensharing } = useUrlParams();
useCallViewKeyboardShortcuts(
!feedbackModalState.isOpen,
containerRef1,
toggleMicrophoneMuted,
toggleLocalVideoMuted,
setMicrophoneMuted

View file

@ -28,7 +28,6 @@ import { ReactComponent as OverflowIcon } from "../icons/Overflow.svg";
import { SelectInput } from "../input/SelectInput";
import { useMediaHandler } from "./useMediaHandler";
import {
useKeyboardShortcuts,
useSpatialAudio,
useShowInspector,
useOptInAnalytics,
@ -65,7 +64,6 @@ export const SettingsModal = (props: Props) => {
const [optInAnalytics, setOptInAnalytics] = useOptInAnalytics();
const [developerSettingsTab, setDeveloperSettingsTab] =
useDeveloperSettingsTab();
const [keyboardShortcuts, setKeyboardShortcuts] = useKeyboardShortcuts();
const [newGrid, setNewGrid] = useNewGrid();
const downloadDebugLog = useDownloadDebugLog();
@ -176,21 +174,6 @@ export const SettingsModal = (props: Props) => {
</>
}
>
<h4>Keyboard</h4>
<FieldRow>
<InputField
id="keyboardShortcuts"
label={t("Single-key keyboard shortcuts")}
type="checkbox"
checked={keyboardShortcuts}
description={t(
"Whether to enable single-key keyboard shortcuts, e.g. 'm' to mute/unmute the mic."
)}
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
setKeyboardShortcuts(event.target.checked)
}
/>
</FieldRow>
<h4>Analytics</h4>
<FieldRow>
<InputField

View file

@ -98,9 +98,6 @@ export const useOptInAnalytics = (): DisableableSetting<boolean | null> => {
return [false, null];
};
export const useKeyboardShortcuts = () =>
useSetting("keyboard-shortcuts", true);
export const useNewGrid = () => useSetting("new-grid", false);
export const useDeveloperSettingsTab = () =>

View file

@ -14,47 +14,55 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { useCallback, useState } from "react";
import { RefObject, useCallback, useRef } from "react";
import { getSetting } from "./settings/useSetting";
import { useEventTarget } from "./useEvents";
/**
* Determines whether focus is in the same part of the tree as the given
* element (specifically, if an ancestor or descendant of it is focused).
*/
const mayReceiveKeyEvents = (e: HTMLElement): boolean => {
const focusedElement = document.activeElement;
return (
focusedElement !== null &&
(focusedElement.contains(e) || e.contains(focusedElement))
);
};
export function useCallViewKeyboardShortcuts(
enabled: boolean,
focusElement: RefObject<HTMLElement | null>,
toggleMicrophoneMuted: () => void,
toggleLocalVideoMuted: () => void,
setMicrophoneMuted: (muted: boolean) => void
) {
const [spacebarHeld, setSpacebarHeld] = useState(false);
const spacebarHeld = useRef(false);
// These event handlers are set on the window because we want users to be able
// to trigger them without going to the trouble of focusing something
useEventTarget(
window,
"keydown",
useCallback(
(event: KeyboardEvent) => {
if (!enabled) return;
// Check if keyboard shortcuts are enabled
const keyboardShortcuts = getSetting("keyboard-shortcuts", true);
if (!keyboardShortcuts) {
return;
}
if (focusElement.current === null) return;
if (!mayReceiveKeyEvents(focusElement.current)) return;
if (event.key === "m") {
toggleMicrophoneMuted();
} else if (event.key == "v") {
toggleLocalVideoMuted();
} else if (event.key === " " && !spacebarHeld) {
setSpacebarHeld(true);
} else if (event.key === " " && !spacebarHeld.current) {
spacebarHeld.current = true;
setMicrophoneMuted(false);
}
},
[
enabled,
spacebarHeld,
focusElement,
toggleLocalVideoMuted,
toggleMicrophoneMuted,
setMicrophoneMuted,
setSpacebarHeld,
]
)
);
@ -64,19 +72,15 @@ export function useCallViewKeyboardShortcuts(
"keyup",
useCallback(
(event: KeyboardEvent) => {
if (!enabled) return;
// Check if keyboard shortcuts are enabled
const keyboardShortcuts = getSetting("keyboard-shortcuts", true);
if (!keyboardShortcuts) {
return;
}
if (focusElement.current === null) return;
if (!mayReceiveKeyEvents(focusElement.current)) return;
if (event.key === " ") {
setSpacebarHeld(false);
spacebarHeld.current = false;
setMicrophoneMuted(true);
}
},
[enabled, setMicrophoneMuted, setSpacebarHeld]
[focusElement, setMicrophoneMuted]
)
);
@ -85,9 +89,9 @@ export function useCallViewKeyboardShortcuts(
"blur",
useCallback(() => {
if (spacebarHeld) {
setSpacebarHeld(false);
spacebarHeld.current = false;
setMicrophoneMuted(true);
}
}, [setMicrophoneMuted, setSpacebarHeld, spacebarHeld])
}, [setMicrophoneMuted, spacebarHeld])
);
}