Pārlūkot izejas kodu

Shortcuts fix (#1020)

* fix hotkeys issues

* hotkeys improvements

* Update src/shared/components/VideoPlayer/VideoPlayer.tsx

Co-authored-by: Rafał Pawłow <pawlow.rafal@gmail.com>

* Update src/shared/components/VideoPlayer/VideoPlayer.tsx

* Update src/shared/components/VideoPlayer/VideoPlayer.tsx

* Update src/shared/components/VideoPlayer/VideoPlayer.tsx

* rebase

Co-authored-by: Rafał Pawłow <pawlow.rafal@gmail.com>
Bartosz Dryl 3 gadi atpakaļ
vecāks
revīzija
98141da4b5

+ 1 - 1
src/shared/components/VideoPlayer/ControlsIndicator.tsx

@@ -20,7 +20,7 @@ import {
   ControlsIndicatorTransitions,
   ControlsIndicatorWrapper,
 } from './ControlsIndicator.style'
-import { CustomVideojsEvents } from './videoJsPlayer'
+import { CustomVideojsEvents } from './utils'
 
 import { Text } from '../Text'
 

+ 27 - 1
src/shared/components/VideoPlayer/VideoPlayer.tsx

@@ -35,7 +35,8 @@ import {
   VolumeSlider,
   VolumeSliderContainer,
 } from './VideoPlayer.style'
-import { CustomVideojsEvents, VOLUME_STEP, VideoJsConfig, useVideoJsPlayer } from './videoJsPlayer'
+import { CustomVideojsEvents, VOLUME_STEP, hotkeysHandler } from './utils'
+import { VideoJsConfig, useVideoJsPlayer } from './videoJsPlayer'
 
 export type VideoPlayerProps = {
   nextVideo?: VideoFieldsFragment | null
@@ -75,6 +76,31 @@ const VideoPlayerComponent: React.ForwardRefRenderFunction<HTMLVideoElement, Vid
   const [playerState, setPlayerState] = useState<PlayerState>(null)
   const [isLoaded, setIsLoaded] = useState(false)
 
+  // handle hotkeys
+  useEffect(() => {
+    if (!player || isInBackground) {
+      return
+    }
+
+    const handler = (event: KeyboardEvent) => {
+      if (
+        (document.activeElement?.tagName === 'BUTTON' && event.key === ' ') ||
+        document.activeElement?.tagName === 'INPUT'
+      ) {
+        return
+      }
+
+      const playerReservedKeys = ['k', ' ', 'ArrowLeft', 'ArrowRight', 'j', 'l', 'ArrowUp', 'ArrowDown', 'm', 'f']
+      if (playerReservedKeys.includes(event.key)) {
+        event.preventDefault()
+        hotkeysHandler(event, player)
+      }
+    }
+    document.addEventListener('keydown', handler)
+
+    return () => document.removeEventListener('keydown', handler)
+  }, [isInBackground, player])
+
   // handle error
   useEffect(() => {
     if (!player) {

+ 89 - 0
src/shared/components/VideoPlayer/utils.ts

@@ -0,0 +1,89 @@
+import { VideoJsPlayer } from 'video.js'
+
+export const VOLUME_STEP = 0.1
+
+export enum CustomVideojsEvents {
+  BackwardFiveSec = 'BACKWARD_FIVE_SEC',
+  BackwardTenSec = 'BACKWARD_TEN_SEC',
+  ForwardFiveSec = 'FORWARD_FIVE_SEC',
+  ForwardTenSec = 'FORWARD_TEN_SEC',
+  Muted = 'MUTED',
+  Unmuted = 'UNMUTED',
+  VolumeIncrease = 'VOLUME_INCREASE',
+  VolumeDecrease = 'VOLUME_DECREASE',
+  PlayControl = 'PLAY_CONTROL',
+  PauseControl = 'PAUSE_CONTROL',
+}
+
+export const hotkeysHandler = (event: KeyboardEvent, playerInstance: VideoJsPlayer) => {
+  if (!playerInstance) {
+    return
+  }
+  const currentTime = playerInstance.currentTime()
+  const currentVolume = Number(playerInstance.volume().toFixed(2))
+  const isMuted = playerInstance.muted()
+  const isFullscreen = playerInstance.isFullscreen()
+  const isPaused = playerInstance.paused()
+
+  switch (event.code) {
+    case 'Space':
+    case 'KeyK':
+      if (isPaused) {
+        playerInstance.play()
+        playerInstance.trigger(CustomVideojsEvents.PlayControl)
+      } else {
+        playerInstance.pause()
+        playerInstance.trigger(CustomVideojsEvents.PauseControl)
+      }
+      return
+    case 'ArrowLeft':
+      playerInstance.currentTime(currentTime - 5)
+      playerInstance.trigger(CustomVideojsEvents.BackwardFiveSec)
+      return
+    case 'ArrowRight':
+      playerInstance.currentTime(currentTime + 5)
+      playerInstance.trigger(CustomVideojsEvents.ForwardFiveSec)
+      return
+    case 'KeyJ':
+      playerInstance.currentTime(currentTime - 10)
+      playerInstance.trigger(CustomVideojsEvents.BackwardTenSec)
+      return
+    case 'KeyL':
+      playerInstance.currentTime(currentTime + 10)
+      playerInstance.trigger(CustomVideojsEvents.ForwardTenSec)
+      return
+    case 'ArrowUp':
+      if (playerInstance.muted()) {
+        playerInstance.muted(false)
+      }
+      if (currentVolume <= 1) {
+        playerInstance.volume(Math.min(currentVolume + VOLUME_STEP, 1))
+      }
+      playerInstance.trigger(CustomVideojsEvents.VolumeIncrease)
+      return
+    case 'ArrowDown':
+      if (currentVolume >= 0) {
+        playerInstance.volume(Math.max(currentVolume - VOLUME_STEP, 0))
+      }
+      playerInstance.trigger(CustomVideojsEvents.VolumeDecrease)
+      return
+    case 'KeyM':
+      if (isMuted) {
+        playerInstance.trigger(CustomVideojsEvents.Unmuted)
+        playerInstance.muted(false)
+      } else {
+        playerInstance.trigger(CustomVideojsEvents.Muted)
+        playerInstance.muted(true)
+      }
+      return
+    case 'KeyF':
+      if (isFullscreen) {
+        playerInstance.exitFullscreen()
+      } else {
+        playerInstance.requestFullscreen()
+      }
+      return
+    default:
+      return
+  }
+}

+ 0 - 89
src/shared/components/VideoPlayer/videoJsPlayer.ts

@@ -2,19 +2,6 @@ import { RefObject, useEffect, useRef, useState } from 'react'
 import videojs, { VideoJsPlayer, VideoJsPlayerOptions } from 'video.js'
 import 'video.js/dist/video-js.css'
 
-export enum CustomVideojsEvents {
-  BackwardFiveSec = 'BACKWARD_FIVE_SEC',
-  BackwardTenSec = 'BACKWARD_TEN_SEC',
-  ForwardFiveSec = 'FORWARD_FIVE_SEC',
-  ForwardTenSec = 'FORWARD_TEN_SEC',
-  Muted = 'MUTED',
-  Unmuted = 'UNMUTED',
-  VolumeIncrease = 'VOLUME_INCREASE',
-  VolumeDecrease = 'VOLUME_DECREASE',
-  PlayControl = 'PLAY_CONTROL',
-  PauseControl = 'PAUSE_CONTROL',
-}
-
 export type VideoJsConfig = {
   src?: string | null
   width?: number
@@ -31,78 +18,6 @@ export type VideoJsConfig = {
   onTimeUpdated?: (time: number) => void
 }
 
-export const VOLUME_STEP = 0.1
-
-const hotkeysHandler = (event: videojs.KeyboardEvent, playerInstance: VideoJsPlayer) => {
-  const currentTime = playerInstance.currentTime()
-  const currentVolume = Number(playerInstance.volume().toFixed(2))
-  const isMuted = playerInstance.muted()
-  const isFullscreen = playerInstance.isFullscreen()
-  const isPaused = playerInstance.paused()
-
-  switch (event.code) {
-    case 'Space':
-    case 'KeyK':
-      if (isPaused) {
-        playerInstance.play()
-        playerInstance.trigger(CustomVideojsEvents.PlayControl)
-      } else {
-        playerInstance.pause()
-        playerInstance.trigger(CustomVideojsEvents.PauseControl)
-      }
-      return
-    case 'ArrowLeft':
-      playerInstance.currentTime(currentTime - 5)
-      playerInstance.trigger(CustomVideojsEvents.BackwardFiveSec)
-      return
-    case 'ArrowRight':
-      playerInstance.currentTime(currentTime + 5)
-      playerInstance.trigger(CustomVideojsEvents.ForwardFiveSec)
-      return
-    case 'KeyJ':
-      playerInstance.currentTime(currentTime - 10)
-      playerInstance.trigger(CustomVideojsEvents.BackwardTenSec)
-      return
-    case 'KeyL':
-      playerInstance.currentTime(currentTime + 10)
-      playerInstance.trigger(CustomVideojsEvents.ForwardTenSec)
-      return
-    case 'ArrowUp':
-      if (playerInstance.muted()) {
-        playerInstance.muted(false)
-      }
-      if (currentVolume <= 1) {
-        playerInstance.volume(Math.min(currentVolume + VOLUME_STEP, 1))
-      }
-      playerInstance.trigger(CustomVideojsEvents.VolumeIncrease)
-      return
-    case 'ArrowDown':
-      if (currentVolume >= 0) {
-        playerInstance.volume(Math.max(currentVolume - VOLUME_STEP, 0))
-      }
-      playerInstance.trigger(CustomVideojsEvents.VolumeDecrease)
-      return
-    case 'KeyM':
-      if (isMuted) {
-        playerInstance.trigger(CustomVideojsEvents.Unmuted)
-        playerInstance.muted(false)
-      } else {
-        playerInstance.trigger(CustomVideojsEvents.Muted)
-        playerInstance.muted(true)
-      }
-      return
-    case 'KeyF':
-      if (isFullscreen) {
-        playerInstance.exitFullscreen()
-      } else {
-        playerInstance.requestFullscreen()
-      }
-      return
-    default:
-      return
-  }
-}
-
 type VideoJsPlayerHook = (config: VideoJsConfig) => [VideoJsPlayer | null, RefObject<HTMLVideoElement>]
 export const useVideoJsPlayer: VideoJsPlayerHook = ({
   fill,
@@ -132,9 +47,6 @@ export const useVideoJsPlayer: VideoJsPlayerHook = ({
       playsinline: true,
       loadingSpinner: false,
       bigPlayButton: false,
-      userActions: {
-        hotkeys: (event) => hotkeysHandler(event, playerInstance),
-      },
       controlBar: {
         // hide all videojs controls besides progress bar
         children: [],
@@ -147,7 +59,6 @@ export const useVideoJsPlayer: VideoJsPlayerHook = ({
     const playerInstance = videojs(playerRef.current as Element, videoJsOptions)
 
     setPlayer(playerInstance)
-    playerRef.current.focus()
 
     return () => {
       playerInstance.dispose()

+ 1 - 20
src/views/viewer/VideoView/VideoView.tsx

@@ -1,10 +1,9 @@
 import { throttle } from 'lodash'
 import React, { useCallback, useEffect, useState } from 'react'
-import { useMatch, useParams } from 'react-router-dom'
+import { useParams } from 'react-router-dom'
 
 import { useAddVideoView, useVideo } from '@/api/hooks'
 import { ChannelLink, InfiniteVideoGrid } from '@/components'
-import { absoluteRoutes } from '@/config/routes'
 import knownLicenses from '@/data/knownLicenses.json'
 import { useRouterQuery } from '@/hooks'
 import { AssetType, useAsset, usePersonalDataStore } from '@/providers'
@@ -44,7 +43,6 @@ export const VideoView: React.FC = () => {
   })
   const { url: mediaUrl } = useAsset({ entity: video, assetType: AssetType.MEDIA })
 
-  const videoRouteMatch = useMatch({ path: absoluteRoutes.viewer.video(id) })
   const [startTimestamp, setStartTimestamp] = useState<number>()
   useEffect(() => {
     if (startTimestamp != null) {
@@ -66,23 +64,6 @@ export const VideoView: React.FC = () => {
   const channelId = video?.channel.id
   const videoId = video?.id
 
-  const handleUserKeyPress = useCallback(
-    (event: Event) => {
-      if (videoRouteMatch) {
-        event.preventDefault()
-      }
-    },
-    [videoRouteMatch]
-  )
-
-  useEffect(() => {
-    window.addEventListener('keydown', handleUserKeyPress)
-
-    return () => {
-      window.removeEventListener('keydown', handleUserKeyPress)
-    }
-  }, [handleUserKeyPress])
-
   useEffect(() => {
     if (!videoId || !channelId) {
       return