Skip to content

Commit

Permalink
Shortcuts fix (#1020)
Browse files Browse the repository at this point in the history
* fix hotkeys issues

* hotkeys improvements

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

Co-authored-by: Rafał Pawłow <[email protected]>

* 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 <[email protected]>
  • Loading branch information
drillprop and rafalpawlow committed Jul 26, 2021
1 parent 1788cc7 commit 306cabb
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 111 deletions.
2 changes: 1 addition & 1 deletion src/shared/components/VideoPlayer/ControlsIndicator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
ControlsIndicatorTransitions,
ControlsIndicatorWrapper,
} from './ControlsIndicator.style'
import { CustomVideojsEvents } from './videoJsPlayer'
import { CustomVideojsEvents } from './utils'

import { Text } from '../Text'

Expand Down
28 changes: 27 additions & 1 deletion src/shared/components/VideoPlayer/VideoPlayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down
89 changes: 89 additions & 0 deletions src/shared/components/VideoPlayer/utils.ts
Original file line number Diff line number Diff line change
@@ -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
}
}
89 changes: 0 additions & 89 deletions src/shared/components/VideoPlayer/videoJsPlayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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: [],
Expand All @@ -147,7 +59,6 @@ export const useVideoJsPlayer: VideoJsPlayerHook = ({
const playerInstance = videojs(playerRef.current as Element, videoJsOptions)

setPlayer(playerInstance)
playerRef.current.focus()

return () => {
playerInstance.dispose()
Expand Down
21 changes: 1 addition & 20 deletions src/views/viewer/VideoView/VideoView.tsx
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -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) {
Expand All @@ -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
Expand Down

0 comments on commit 306cabb

Please sign in to comment.