From b88a10c49629336f5f43f60ecf7968541540bed5 Mon Sep 17 00:00:00 2001 From: Davide Bizzi Date: Fri, 8 Nov 2024 16:29:21 +0100 Subject: [PATCH] feat: add onShortcut prop to PlayerArgs to intercept events --- src/stories/player/_types.tsx | 3 +- .../player/hooks/useKeyboardCommands.ts | 37 +++++++++++++------ src/stories/player/index.stories.tsx | 7 ++-- src/stories/player/index.tsx | 17 ++++++--- 4 files changed, 43 insertions(+), 21 deletions(-) diff --git a/src/stories/player/_types.tsx b/src/stories/player/_types.tsx index d27e45dc..2fcd9729 100644 --- a/src/stories/player/_types.tsx +++ b/src/stories/player/_types.tsx @@ -12,6 +12,7 @@ export interface PlayerArgs extends HTMLAttributes { handleBookmarkUpdate?: (bookmark: IBookmark) => void; i18n?: PlayerI18n; showControls?: boolean; + onShortcut?: (type: string) => void; } export interface PlayerI18n { @@ -48,4 +49,4 @@ type VideoTag = { style: string; usageNumber: number; }; -}; \ No newline at end of file +}; diff --git a/src/stories/player/hooks/useKeyboardCommands.ts b/src/stories/player/hooks/useKeyboardCommands.ts index b0922f83..ba5c77f3 100644 --- a/src/stories/player/hooks/useKeyboardCommands.ts +++ b/src/stories/player/hooks/useKeyboardCommands.ts @@ -1,49 +1,64 @@ -import { useVideoContext } from "@appquality/stream-player"; import { useEffect } from "react"; -type KeyboardCommandsHook = ( - setIsPlaying: (isPlaying: boolean) => void, - onCutHandler?: (time: number) => void, - videoRef?: HTMLVideoElement | null, -) => void; +type KeyboardCommandsHook = ({ + setIsPlaying, + onCutHandler, + videoRef, +}: { + setIsPlaying: (isPlaying: boolean) => void; + onCutHandler?: (time: number) => void; + onShortcut?: (type: string) => void; + videoRef?: HTMLVideoElement | null; +}) => void; -export const useKeyboardCommands: KeyboardCommandsHook = (setIsPlaying, onCutHandler, videoRef) => { +export const useKeyboardCommands: KeyboardCommandsHook = ({ + setIsPlaying, + onCutHandler, + onShortcut, + videoRef, +}) => { useEffect(() => { function handleKeyDown(e: KeyboardEvent) { // console.log("handleKeyDown", e.code, document.activeElement, e.target); if (document.activeElement?.tagName === "INPUT") return; if (document.activeElement?.tagName === "TEXTAREA") return; - + if (!videoRef) return; - + if (e.code === "Space") { e.preventDefault(); if (videoRef.paused) { setIsPlaying(true); videoRef.play(); + onShortcut?.("play"); } else { setIsPlaying(false); videoRef.pause(); + onShortcut?.("pause"); } } if (e.code === "ArrowLeft") { videoRef.currentTime -= 10; + onShortcut?.("rewind"); } if (e.code === "ArrowRight") { videoRef.currentTime += 10; + onShortcut?.("fast-forward"); } if (e.code === "KeyM") { videoRef.muted = !videoRef.muted; videoRef.volume = videoRef.muted ? 0 : 1; + onShortcut?.("mute"); } if (e.code === "KeyS") { onCutHandler?.(videoRef.currentTime); e.stopPropagation(); + onShortcut?.("start/stop_observation"); } } document.addEventListener("keydown", handleKeyDown); return () => { document.removeEventListener("keydown", handleKeyDown); - } + }; }, [videoRef, onCutHandler]); -} \ No newline at end of file +}; diff --git a/src/stories/player/index.stories.tsx b/src/stories/player/index.stories.tsx index 8409aca5..d2f8c3cc 100644 --- a/src/stories/player/index.stories.tsx +++ b/src/stories/player/index.stories.tsx @@ -16,6 +16,7 @@ interface PlayerStoryArgs extends PlayerArgs {} const defaultArgs: PlayerStoryArgs = { url: "https://s3.eu-west-1.amazonaws.com/appq.static/demo/098648899205a00f8311d929d3073499ef9d664b_1715352138.mp4", onCutHandler: undefined, // Storybook fix https://github.com/storybookjs/storybook/issues/22930 + onShortcut: (type: string) => console.log("Shortcut intercept", type), }; const Template: StoryFn = (args) => ( @@ -37,7 +38,7 @@ const TemplateWithCutter: StoryFn = ({ ...args }) => { const [observations, setObservations] = useState( - bookmarks || [] + bookmarks || [], ); const [start, setStart] = useState(undefined); @@ -60,7 +61,7 @@ const TemplateWithCutter: StoryFn = ({ ]); setStart(undefined); }, - [observations, start] + [observations, start], ); return ( @@ -105,7 +106,7 @@ const TemplateWithButtonForPip: StoryFn = (args) => { (isPipFromPlayer: boolean) => { setIsPip(isPipFromPlayer); }, - [setIsPip] + [setIsPip], ); return ( diff --git a/src/stories/player/index.tsx b/src/stories/player/index.tsx index cfff8d91..1c277dd5 100644 --- a/src/stories/player/index.tsx +++ b/src/stories/player/index.tsx @@ -7,13 +7,13 @@ import { useRef, } from "react"; import { PlayerArgs } from "./_types"; +import { ProgressContextProvider } from "./context/progressContext"; +import { useKeyboardCommands } from "./hooks/useKeyboardCommands"; +import { usePictureInPicture } from "./hooks/usePictureInPicture"; import { Container } from "./parts/container"; import { Controls } from "./parts/controls"; import { FloatingControls } from "./parts/floatingControls"; import { VideoSpinner } from "./parts/spinner"; -import { ProgressContextProvider } from "./context/progressContext"; -import { usePictureInPicture } from "./hooks/usePictureInPicture"; -import { useKeyboardCommands } from "./hooks/useKeyboardCommands"; /** * The Player is a styled media tag with custom controls @@ -39,7 +39,12 @@ const PlayerCore = forwardRef( videoRef, ]); - useKeyboardCommands(setIsPlaying, onCutHandler, videoRef); + useKeyboardCommands({ + setIsPlaying, + onCutHandler, + videoRef, + onShortcut: props.onShortcut, + }); usePictureInPicture(videoRef, pipMode, onPipChange); useEffect(() => { @@ -85,7 +90,7 @@ const PlayerCore = forwardRef( ); - } + }, ); const PlayerProvider = (props: PropsWithChildren) => ( @@ -96,4 +101,4 @@ const PlayerProvider = (props: PropsWithChildren) => ( PlayerProvider.Core = PlayerCore; -export { Player, PlayerProvider, useVideoContext }; \ No newline at end of file +export { Player, PlayerProvider, useVideoContext };