From 359630518018672f2e1574660397631f3f95db44 Mon Sep 17 00:00:00 2001 From: Martin <901824+martinstark@users.noreply.github.com> Date: Wed, 17 Apr 2024 12:53:35 +0200 Subject: [PATCH] feat: add mute input hotkeys --- package.json | 1 + .../production-line/production-line.tsx | 74 +++++++++++++------ .../production-line/use-line-hotkeys.ts | 27 +++++++ yarn.lock | 5 ++ 4 files changed, 83 insertions(+), 24 deletions(-) create mode 100644 src/components/production-line/use-line-hotkeys.ts diff --git a/package.json b/package.json index d5f9c9f8..30d52101 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-hook-form": "^7.51.3", + "react-hotkeys-hook": "^4.5.0", "react-router-dom": "^6.22.3" }, "devDependencies": { diff --git a/src/components/production-line/production-line.tsx b/src/components/production-line/production-line.tsx index b95f0775..41ffb258 100644 --- a/src/components/production-line/production-line.tsx +++ b/src/components/production-line/production-line.tsx @@ -1,5 +1,5 @@ import styled from "@emotion/styled"; -import { FC, useEffect, useState } from "react"; +import { FC, useCallback, useEffect, useState } from "react"; import { useNavigate, useParams } from "react-router-dom"; import { useGlobalState } from "../../global-state/context-provider.tsx"; import { useAudioInput } from "./use-audio-input.ts"; @@ -17,6 +17,8 @@ import { DisplayContainer, FlexContainer } from "../generic-components.ts"; import { useHeartbeat } from "./use-heartbeat.ts"; import { JoinProduction } from "../landing-page/join-production.tsx"; import { useDeviceLabels } from "./use-device-labels.ts"; +import { useLineHotkeys } from "./use-line-hotkeys.ts"; +import { isMobile } from "../../bowser.ts"; const TempDiv = styled.div` padding: 1rem 0; @@ -51,10 +53,9 @@ const UserControlBtn = styled(ActionButton)` export const ProductionLine: FC = () => { const { productionId: paramProductionId, lineId: paramLineId } = useParams(); - const [{ joinProductionOptions, mediaStreamInput }, dispatch] = - useGlobalState(); + const [{ joinProductionOptions }, dispatch] = useGlobalState(); const navigate = useNavigate(); - const [micMute, setMicMute] = useState(true); + const [isInputMuted, setIsInputMuted] = useState(true); const [line, setLine] = useState(null); const [loading, setLoading] = useState(true); @@ -64,6 +65,24 @@ export const ProductionLine: FC = () => { inputId: joinProductionOptions?.audioinput ?? null, }); + const muteInput = useCallback( + (mute: boolean) => { + if (inputAudioStream && inputAudioStream !== "no-device") { + inputAudioStream.getTracks().forEach((t) => { + // eslint-disable-next-line no-param-reassign + t.enabled = !mute; + setIsInputMuted(mute); + }); + } + }, + [inputAudioStream] + ); + + useLineHotkeys({ + muteInput, + isInputMuted, + }); + const { sessionId, sdpOffer } = useEstablishSession({ joinProductionOptions, dispatch, @@ -129,15 +148,6 @@ export const ProductionLine: FC = () => { navigate("/"); }; - useEffect(() => { - if (mediaStreamInput) { - mediaStreamInput.getTracks().forEach((track) => { - // eslint-disable-next-line no-param-reassign - track.enabled = !micMute; - }); - } - }, [mediaStreamInput, micMute]); - const deviceLabels = useDeviceLabels({ joinProductionOptions }); // Check if we have what's needed to join a production line @@ -206,17 +216,19 @@ export const ProductionLine: FC = () => {
Controls - - setMicMute(!micMute)} - > - - {micMute ? : } - - {micMute ? "Muted" : "Unmuted"} - - + {inputAudioStream && inputAudioStream !== "no-device" && ( + + muteInput(!isInputMuted)} + > + + {isInputMuted ? : } + + {isInputMuted ? "Muted" : "Unmuted"} + + + )} {deviceLabels?.inputLabel && ( @@ -229,6 +241,20 @@ export const ProductionLine: FC = () => { Audio Output: {deviceLabels.outputLabel} )} + + {!isMobile && ( + <> + + Hotkeys + + + M: Toggle Input Mute + + + T: Push to Talk + + + )}
diff --git a/src/components/production-line/use-line-hotkeys.ts b/src/components/production-line/use-line-hotkeys.ts new file mode 100644 index 00000000..b4b468c4 --- /dev/null +++ b/src/components/production-line/use-line-hotkeys.ts @@ -0,0 +1,27 @@ +import { useHotkeys } from "react-hotkeys-hook"; + +type TProps = { + muteInput: (mute: boolean) => void; + isInputMuted: boolean; +}; + +export const useLineHotkeys = ({ muteInput, isInputMuted }: TProps) => { + useHotkeys("m", () => { + muteInput(!isInputMuted); + }); + + useHotkeys( + "t", + (e) => { + if (e.type === "keydown") { + muteInput(false); + } else { + muteInput(true); + } + }, + { + keyup: true, + keydown: true, + } + ); +}; diff --git a/yarn.lock b/yarn.lock index bc7424db..add827ba 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3042,6 +3042,11 @@ react-hook-form@^7.51.3: resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.51.3.tgz#7486dd2d52280b6b28048c099a98d2545931cab3" integrity sha512-cvJ/wbHdhYx8aviSWh28w9ImjmVsb5Y05n1+FW786vEZQJV5STNM0pW6ujS+oiBecb0ARBxJFyAnXj9+GHXACQ== +react-hotkeys-hook@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/react-hotkeys-hook/-/react-hotkeys-hook-4.5.0.tgz#807b389b15256daf6a813a1ec09e6698064fe97f" + integrity sha512-Samb85GSgAWFQNvVt3PS90LPPGSf9mkH/r4au81ZP1yOIFayLC3QAvqTgGtJ8YEDMXtPmaVBs6NgipHO6h4Mug== + react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"