From cb85733426cde498813697ea59f4514750da8f0e Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 11 Nov 2022 15:53:58 +0000 Subject: [PATCH 01/11] Add 'm' and 'space' shortcuts for mute/unmuting during a call --- src/room/useGroupCall.ts | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/room/useGroupCall.ts b/src/room/useGroupCall.ts index 3a917f95..cc39dd8e 100644 --- a/src/room/useGroupCall.ts +++ b/src/room/useGroupCall.ts @@ -298,10 +298,14 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType { PosthogAnalytics.instance.eventMuteCamera.track(toggleToMute); }, [groupCall]); + const setMicrophoneMuted = useCallback((setMuted) => { + groupCall.setMicrophoneMuted(setMuted); + PosthogAnalytics.instance.eventMuteMicrophone.track(setMuted); + }, [groupCall]); + const toggleMicrophoneMuted = useCallback(() => { const toggleToMute = !groupCall.isMicrophoneMuted(); - groupCall.setMicrophoneMuted(toggleToMute); - PosthogAnalytics.instance.eventMuteMicrophone.track(toggleToMute); + setMicrophoneMuted(toggleToMute); }, [groupCall]); const toggleScreensharing = useCallback(async () => { @@ -395,6 +399,33 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType { } }, [t]); + + useEffect(() => { + const keyDownListener = (event) => { + if (event.key === "m") { + toggleMicrophoneMuted(); + } + if (event.key === " ") { + setMicrophoneMuted(false); + } + }; + + const keyUpListener = (event) => { + if (event.key === " ") { + setMicrophoneMuted(true); + } + }; + + window.addEventListener("keydown", keyDownListener, true); + window.addEventListener("keyup", keyUpListener, true); + + return () => { + window.removeEventListener("keydown", keyDownListener, true); + window.removeEventListener("keyup", keyUpListener, true); + } + }, [toggleMicrophoneMuted, setMicrophoneMuted]); + + return { state, calls, From 456194312b75acf2d912fdad6e76b877595ab22d Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 11 Nov 2022 16:00:32 +0000 Subject: [PATCH 02/11] Lint --- src/config/Config.ts | 6 +++++- src/room/useGroupCall.ts | 15 ++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/config/Config.ts b/src/config/Config.ts index 42d9b1b1..2806a5b0 100644 --- a/src/config/Config.ts +++ b/src/config/Config.ts @@ -14,7 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { DEFAULT_CONFIG, ConfigOptions, ResolvedConfigOptions } from "./ConfigOptions"; +import { + DEFAULT_CONFIG, + ConfigOptions, + ResolvedConfigOptions, +} from "./ConfigOptions"; export class Config { private static internalInstance: Config; diff --git a/src/room/useGroupCall.ts b/src/room/useGroupCall.ts index cc39dd8e..8a6b6b56 100644 --- a/src/room/useGroupCall.ts +++ b/src/room/useGroupCall.ts @@ -298,10 +298,13 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType { PosthogAnalytics.instance.eventMuteCamera.track(toggleToMute); }, [groupCall]); - const setMicrophoneMuted = useCallback((setMuted) => { - groupCall.setMicrophoneMuted(setMuted); - PosthogAnalytics.instance.eventMuteMicrophone.track(setMuted); - }, [groupCall]); + const setMicrophoneMuted = useCallback( + (setMuted) => { + groupCall.setMicrophoneMuted(setMuted); + PosthogAnalytics.instance.eventMuteMicrophone.track(setMuted); + }, + [groupCall] + ); const toggleMicrophoneMuted = useCallback(() => { const toggleToMute = !groupCall.isMicrophoneMuted(); @@ -399,7 +402,6 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType { } }, [t]); - useEffect(() => { const keyDownListener = (event) => { if (event.key === "m") { @@ -422,10 +424,9 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType { return () => { window.removeEventListener("keydown", keyDownListener, true); window.removeEventListener("keyup", keyUpListener, true); - } + }; }, [toggleMicrophoneMuted, setMicrophoneMuted]); - return { state, calls, From c9330debd47b751a80c883badef63ef4056d06dc Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 11 Nov 2022 16:02:24 +0000 Subject: [PATCH 03/11] Lint take2? --- src/room/useGroupCall.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/room/useGroupCall.ts b/src/room/useGroupCall.ts index 8a6b6b56..dd29e62c 100644 --- a/src/room/useGroupCall.ts +++ b/src/room/useGroupCall.ts @@ -309,7 +309,7 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType { const toggleMicrophoneMuted = useCallback(() => { const toggleToMute = !groupCall.isMicrophoneMuted(); setMicrophoneMuted(toggleToMute); - }, [groupCall]); + }, [groupCall, setMicrophoneMuted]); const toggleScreensharing = useCallback(async () => { if (!groupCall.isScreensharing()) { From b30ef953b4fa7ea322cfa124648e8c952ff3bedb Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Mon, 14 Nov 2022 10:21:24 +0000 Subject: [PATCH 04/11] Put keyboard shortcuts behind settings flag --- public/locales/en-GB/app.json | 1 + src/room/useGroupCall.ts | 13 +++++++++++++ src/settings/SettingsModal.tsx | 13 +++++++++++++ src/settings/useSetting.ts | 2 ++ 4 files changed, 29 insertions(+) diff --git a/public/locales/en-GB/app.json b/public/locales/en-GB/app.json index 7d4ae7b3..e51f9367 100644 --- a/public/locales/en-GB/app.json +++ b/public/locales/en-GB/app.json @@ -42,6 +42,7 @@ "Display name": "Display name", "Download debug logs": "Download debug logs", "Element Call Home": "Element Call Home", + "Enable keyboard shortcuts": "Enable keyboard shortcuts", "Entering room…": "Entering room…", "Exit full screen": "Exit full screen", "Fetching group call timed out.": "Fetching group call timed out.", diff --git a/src/room/useGroupCall.ts b/src/room/useGroupCall.ts index dd29e62c..0ad2ce5f 100644 --- a/src/room/useGroupCall.ts +++ b/src/room/useGroupCall.ts @@ -33,6 +33,7 @@ import { usePageUnload } from "./usePageUnload"; import { PosthogAnalytics } from "../PosthogAnalytics"; import { TranslatedError, translatedError } from "../TranslatedError"; import { ElementWidgetActions, ScreenshareStartData, widget } from "../widget"; +import { getSetting } from "../settings/useSetting"; export interface UseGroupCallReturnType { state: GroupCallState; @@ -404,6 +405,12 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType { useEffect(() => { const keyDownListener = (event) => { + // Check if keyboard shortcuts are enabled + const keyboardShortcuts = getSetting("keyboard-shortcuts", true); + if (!keyboardShortcuts) { + return; + } + if (event.key === "m") { toggleMicrophoneMuted(); } @@ -413,6 +420,12 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType { }; const keyUpListener = (event) => { + // Check if keyboard shortcuts are enabled + const keyboardShortcuts = getSetting("keyboard-shortcuts", true); + if (!keyboardShortcuts) { + return; + } + if (event.key === " ") { setMicrophoneMuted(true); } diff --git a/src/settings/SettingsModal.tsx b/src/settings/SettingsModal.tsx index f895538a..143d6a64 100644 --- a/src/settings/SettingsModal.tsx +++ b/src/settings/SettingsModal.tsx @@ -28,6 +28,7 @@ import { ReactComponent as OverflowIcon } from "../icons/Overflow.svg"; import { SelectInput } from "../input/SelectInput"; import { useMediaHandler } from "./useMediaHandler"; import { + useKeyboardShortcuts, useSpatialAudio, useShowInspector, useOptInAnalytics, @@ -59,6 +60,7 @@ export const SettingsModal = (props: Props) => { const [spatialAudio, setSpatialAudio] = useSpatialAudio(); const [showInspector, setShowInspector] = useShowInspector(); const [optInAnalytics, setOptInAnalytics] = useOptInAnalytics(); + const [keyboardShortcuts, setKeyboardShortcuts] = useKeyboardShortcuts(); const downloadDebugLog = useDownloadDebugLog(); @@ -166,6 +168,17 @@ export const SettingsModal = (props: Props) => { } /> + + ) => + setKeyboardShortcuts(event.target.checked) + } + /> + (name: string, defaultValue: T): T => { export const useSpatialAudio = () => useSetting("spatial-audio", false); export const useShowInspector = () => useSetting("show-inspector", false); export const useOptInAnalytics = () => useSetting("opt-in-analytics", false); +export const useKeyboardShortcuts = () => + useSetting("keyboard-shortcuts", true); From c67e7ebc2ca151697b9aec5fed59ce7f63f5cb3d Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Mon, 14 Nov 2022 10:40:02 +0000 Subject: [PATCH 05/11] Put PTTButton shortcuts behind the new shortcut --- src/room/PTTButton.tsx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/room/PTTButton.tsx b/src/room/PTTButton.tsx index 3551cfa2..e083759b 100644 --- a/src/room/PTTButton.tsx +++ b/src/room/PTTButton.tsx @@ -23,6 +23,7 @@ import { ReactComponent as MicIcon } from "../icons/Mic.svg"; import { useEventTarget } from "../useEvents"; import { Avatar } from "../Avatar"; import { usePrefersReducedMotion } from "../usePrefersReducedMotion"; +import { getSetting } from "../settings/useSetting"; interface Props { enabled: boolean; @@ -134,6 +135,12 @@ export const PTTButton: React.FC = ({ (e: KeyboardEvent) => { if (e.code === "Space") { if (!enabled) return; + // Check if keyboard shortcuts are enabled + const keyboardShortcuts = getSetting("keyboard-shortcuts", true); + if (!keyboardShortcuts) { + return; + } + e.preventDefault(); hold(); @@ -148,6 +155,12 @@ export const PTTButton: React.FC = ({ useCallback( (e: KeyboardEvent) => { if (e.code === "Space") { + // Check if keyboard shortcuts are enabled + const keyboardShortcuts = getSetting("keyboard-shortcuts", true); + if (!keyboardShortcuts) { + return; + } + e.preventDefault(); unhold(); From 0aadb7e60cf8a10fcd1fb7664ec017451b770b3c Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Mon, 14 Nov 2022 11:29:42 +0000 Subject: [PATCH 06/11] Add a straw man description to the setting --- public/locales/en-GB/app.json | 1 + src/settings/SettingsModal.tsx | 3 +++ 2 files changed, 4 insertions(+) diff --git a/public/locales/en-GB/app.json b/public/locales/en-GB/app.json index e51f9367..5e780130 100644 --- a/public/locales/en-GB/app.json +++ b/public/locales/en-GB/app.json @@ -134,6 +134,7 @@ "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 the keyboard shortcuts to e.g. mute/unmute the local microphone.": "Whether to enable the keyboard shortcuts to e.g. mute/unmute the local microphone.", "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" diff --git a/src/settings/SettingsModal.tsx b/src/settings/SettingsModal.tsx index 143d6a64..3b90fd85 100644 --- a/src/settings/SettingsModal.tsx +++ b/src/settings/SettingsModal.tsx @@ -174,6 +174,9 @@ export const SettingsModal = (props: Props) => { label={t("Enable keyboard shortcuts")} type="checkbox" checked={keyboardShortcuts} + description={t( + "Whether to enable the keyboard shortcuts to e.g. mute/unmute the local microphone." + )} onChange={(event: React.ChangeEvent) => setKeyboardShortcuts(event.target.checked) } From 4cd49dee4b1ac42c79d57c0a847903f7cd790129 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Mon, 14 Nov 2022 16:11:32 +0000 Subject: [PATCH 07/11] Update description --- public/locales/en-GB/app.json | 2 +- src/settings/SettingsModal.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/locales/en-GB/app.json b/public/locales/en-GB/app.json index 5e780130..55abd4e0 100644 --- a/public/locales/en-GB/app.json +++ b/public/locales/en-GB/app.json @@ -134,7 +134,7 @@ "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 the keyboard shortcuts to e.g. mute/unmute the local microphone.": "Whether to enable the keyboard shortcuts to e.g. mute/unmute the local microphone.", + "Whether to enable the keyboard shortcuts, e.g. 'm' to mute/unmute the mic.": "Whether to enable the 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" diff --git a/src/settings/SettingsModal.tsx b/src/settings/SettingsModal.tsx index 3b90fd85..c896497c 100644 --- a/src/settings/SettingsModal.tsx +++ b/src/settings/SettingsModal.tsx @@ -175,7 +175,7 @@ export const SettingsModal = (props: Props) => { type="checkbox" checked={keyboardShortcuts} description={t( - "Whether to enable the keyboard shortcuts to e.g. mute/unmute the local microphone." + "Whether to enable the keyboard shortcuts, e.g. 'm' to mute/unmute the mic." )} onChange={(event: React.ChangeEvent) => setKeyboardShortcuts(event.target.checked) From 67b97e63ca8f4c82e76311df72ad346b2a0a44bd Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Mon, 14 Nov 2022 16:05:49 +0000 Subject: [PATCH 08/11] Use useEventTarget --- src/room/useGroupCall.ts | 71 +++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 33 deletions(-) diff --git a/src/room/useGroupCall.ts b/src/room/useGroupCall.ts index 0ad2ce5f..f8da7e28 100644 --- a/src/room/useGroupCall.ts +++ b/src/room/useGroupCall.ts @@ -34,6 +34,7 @@ import { PosthogAnalytics } from "../PosthogAnalytics"; import { TranslatedError, translatedError } from "../TranslatedError"; import { ElementWidgetActions, ScreenshareStartData, widget } from "../widget"; import { getSetting } from "../settings/useSetting"; +import { useEventTarget } from "../useEvents"; export interface UseGroupCallReturnType { state: GroupCallState; @@ -403,42 +404,46 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType { } }, [t]); - useEffect(() => { - const keyDownListener = (event) => { - // Check if keyboard shortcuts are enabled - const keyboardShortcuts = getSetting("keyboard-shortcuts", true); - if (!keyboardShortcuts) { - return; - } + useEventTarget( + window, + "keydown", + useCallback( + (event: KeyboardEvent) => { + // Check if keyboard shortcuts are enabled + const keyboardShortcuts = getSetting("keyboard-shortcuts", true); + if (!keyboardShortcuts) { + return; + } - if (event.key === "m") { - toggleMicrophoneMuted(); - } - if (event.key === " ") { - setMicrophoneMuted(false); - } - }; + if (event.key === "m") { + toggleMicrophoneMuted(); + } + if (event.key === " ") { + setMicrophoneMuted(false); + } + }, + [toggleMicrophoneMuted, setMicrophoneMuted] + ) + ); - const keyUpListener = (event) => { - // Check if keyboard shortcuts are enabled - const keyboardShortcuts = getSetting("keyboard-shortcuts", true); - if (!keyboardShortcuts) { - return; - } + useEventTarget( + window, + "keyup", + useCallback( + (event: KeyboardEvent) => { + // Check if keyboard shortcuts are enabled + const keyboardShortcuts = getSetting("keyboard-shortcuts", true); + if (!keyboardShortcuts) { + return; + } - if (event.key === " ") { - setMicrophoneMuted(true); - } - }; - - window.addEventListener("keydown", keyDownListener, true); - window.addEventListener("keyup", keyUpListener, true); - - return () => { - window.removeEventListener("keydown", keyDownListener, true); - window.removeEventListener("keyup", keyUpListener, true); - }; - }, [toggleMicrophoneMuted, setMicrophoneMuted]); + if (event.key === " ") { + setMicrophoneMuted(true); + } + }, + [setMicrophoneMuted] + ) + ); return { state, From d09c3d83747f324a8cc54967bf9b3facdcc1d578 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Mon, 14 Nov 2022 16:14:16 +0000 Subject: [PATCH 09/11] Add 'v' shortcut --- src/room/useGroupCall.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/room/useGroupCall.ts b/src/room/useGroupCall.ts index f8da7e28..e06787ca 100644 --- a/src/room/useGroupCall.ts +++ b/src/room/useGroupCall.ts @@ -417,12 +417,13 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType { if (event.key === "m") { toggleMicrophoneMuted(); - } - if (event.key === " ") { + } else if (event.key == "v") { + toggleLocalVideoMuted(); + } else if (event.key === " ") { setMicrophoneMuted(false); } }, - [toggleMicrophoneMuted, setMicrophoneMuted] + [toggleLocalVideoMuted, toggleMicrophoneMuted, setMicrophoneMuted] ) ); From 6855e61c47b36099ed4d592bf077c2c7adf115fe Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Mon, 14 Nov 2022 20:55:24 +0000 Subject: [PATCH 10/11] Correctly handle window losing focus --- src/room/useGroupCall.ts | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/room/useGroupCall.ts b/src/room/useGroupCall.ts index e06787ca..14e40d14 100644 --- a/src/room/useGroupCall.ts +++ b/src/room/useGroupCall.ts @@ -404,6 +404,8 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType { } }, [t]); + const [spacebarHeld, setSpacebarHeld] = useState(false); + useEventTarget( window, "keydown", @@ -420,10 +422,16 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType { } else if (event.key == "v") { toggleLocalVideoMuted(); } else if (event.key === " ") { + setSpacebarHeld(true); setMicrophoneMuted(false); } }, - [toggleLocalVideoMuted, toggleMicrophoneMuted, setMicrophoneMuted] + [ + toggleLocalVideoMuted, + toggleMicrophoneMuted, + setMicrophoneMuted, + setSpacebarHeld, + ] ) ); @@ -439,13 +447,25 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType { } if (event.key === " ") { + setSpacebarHeld(false); setMicrophoneMuted(true); } }, - [setMicrophoneMuted] + [setMicrophoneMuted, setSpacebarHeld] ) ); + useEventTarget( + window, + "blur", + useCallback(() => { + if (spacebarHeld) { + setSpacebarHeld(false); + setMicrophoneMuted(true); + } + }, [setMicrophoneMuted, setSpacebarHeld, spacebarHeld]) + ); + return { state, calls, From a2bbe612928e534ce6bc1ab9b99960123ee8c4ad Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Mon, 14 Nov 2022 20:57:36 +0000 Subject: [PATCH 11/11] Update help text --- public/locales/en-GB/app.json | 4 ++-- src/settings/SettingsModal.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/public/locales/en-GB/app.json b/public/locales/en-GB/app.json index 55abd4e0..871567b8 100644 --- a/public/locales/en-GB/app.json +++ b/public/locales/en-GB/app.json @@ -42,7 +42,7 @@ "Display name": "Display name", "Download debug logs": "Download debug logs", "Element Call Home": "Element Call Home", - "Enable keyboard shortcuts": "Enable keyboard shortcuts", + "Single-key keyboard shortcuts": "Single-key keyboard shortcuts", "Entering room…": "Entering room…", "Exit full screen": "Exit full screen", "Fetching group call timed out.": "Fetching group call timed out.", @@ -134,7 +134,7 @@ "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 the keyboard shortcuts, e.g. 'm' to mute/unmute the mic.": "Whether to enable the 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.": "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" diff --git a/src/settings/SettingsModal.tsx b/src/settings/SettingsModal.tsx index c896497c..5bf02e78 100644 --- a/src/settings/SettingsModal.tsx +++ b/src/settings/SettingsModal.tsx @@ -171,11 +171,11 @@ export const SettingsModal = (props: Props) => { ) => setKeyboardShortcuts(event.target.checked)