You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
152 lines
4.6 KiB
152 lines
4.6 KiB
import { useEffect, useRef, useState } from "react"; |
|
|
|
import { useCaptions } from "@/components/player/hooks/useCaptions"; |
|
import { useVolume } from "@/components/player/hooks/useVolume"; |
|
import { useOverlayRouter } from "@/hooks/useOverlayRouter"; |
|
import { usePlayerStore } from "@/stores/player/store"; |
|
import { useEmpheralVolumeStore } from "@/stores/volume"; |
|
|
|
export function KeyboardEvents() { |
|
const router = useOverlayRouter(""); |
|
const display = usePlayerStore((s) => s.display); |
|
const mediaProgress = usePlayerStore((s) => s.progress); |
|
const { isSeeking } = usePlayerStore((s) => s.interface); |
|
const mediaPlaying = usePlayerStore((s) => s.mediaPlaying); |
|
const time = usePlayerStore((s) => s.progress.time); |
|
const { setVolume, toggleMute } = useVolume(); |
|
|
|
const { toggleLastUsed } = useCaptions(); |
|
const setShowVolume = useEmpheralVolumeStore((s) => s.setShowVolume); |
|
|
|
const [isRolling, setIsRolling] = useState(false); |
|
const volumeDebounce = useRef<ReturnType<typeof setTimeout> | undefined>(); |
|
|
|
const dataRef = useRef({ |
|
setShowVolume, |
|
setVolume, |
|
toggleMute, |
|
setIsRolling, |
|
toggleLastUsed, |
|
display, |
|
mediaPlaying, |
|
mediaProgress, |
|
isSeeking, |
|
isRolling, |
|
time, |
|
router, |
|
}); |
|
useEffect(() => { |
|
dataRef.current = { |
|
setShowVolume, |
|
setVolume, |
|
toggleMute, |
|
setIsRolling, |
|
toggleLastUsed, |
|
display, |
|
mediaPlaying, |
|
mediaProgress, |
|
isSeeking, |
|
isRolling, |
|
time, |
|
router, |
|
}; |
|
}, [ |
|
setShowVolume, |
|
setVolume, |
|
toggleMute, |
|
setIsRolling, |
|
toggleLastUsed, |
|
display, |
|
mediaPlaying, |
|
mediaProgress, |
|
isSeeking, |
|
isRolling, |
|
time, |
|
router, |
|
]); |
|
|
|
useEffect(() => { |
|
const keyEventHandler = (evt: KeyboardEvent) => { |
|
if (evt.target && (evt.target as HTMLInputElement).nodeName === "INPUT") |
|
return; |
|
|
|
const k = evt.key; |
|
|
|
// Volume |
|
if (["ArrowUp", "ArrowDown", "m"].includes(k)) { |
|
dataRef.current.setShowVolume(true); |
|
|
|
if (volumeDebounce.current) clearTimeout(volumeDebounce.current); |
|
volumeDebounce.current = setTimeout(() => { |
|
dataRef.current.setShowVolume(false); |
|
}, 3e3); |
|
} |
|
if (k === "ArrowUp") |
|
dataRef.current.setVolume( |
|
(dataRef.current.mediaPlaying?.volume || 0) + 0.15, |
|
); |
|
if (k === "ArrowDown") |
|
dataRef.current.setVolume( |
|
(dataRef.current.mediaPlaying?.volume || 0) - 0.15, |
|
); |
|
if (k === "m") dataRef.current.toggleMute(); |
|
|
|
// Video playback speed |
|
if (k === ">" || k === "<") { |
|
const options = [0.25, 0.5, 1, 1.5, 2]; |
|
let idx = options.indexOf(dataRef.current.mediaPlaying?.playbackRate); |
|
if (idx === -1) idx = options.indexOf(1); |
|
const nextIdx = idx + (k === ">" ? 1 : -1); |
|
const next = options[nextIdx]; |
|
if (next) dataRef.current.display?.setPlaybackRate(next); |
|
} |
|
|
|
// Video progress |
|
if (k === "ArrowRight") |
|
dataRef.current.display?.setTime(dataRef.current.time + 5); |
|
if (k === "ArrowLeft") |
|
dataRef.current.display?.setTime(dataRef.current.time - 5); |
|
if (k === "j") |
|
dataRef.current.display?.setTime(dataRef.current.time - 10); |
|
if (k === "l") |
|
dataRef.current.display?.setTime(dataRef.current.time + 10); |
|
if (k === "." && dataRef.current.mediaPlaying?.isPaused) |
|
dataRef.current.display?.setTime(dataRef.current.time + 1); |
|
if (k === "," && dataRef.current.mediaPlaying?.isPaused) |
|
dataRef.current.display?.setTime(dataRef.current.time - 1); |
|
|
|
// Utils |
|
if (k === "f") dataRef.current.display?.toggleFullscreen(); |
|
if (k === " ") |
|
dataRef.current.display?.[ |
|
dataRef.current.mediaPlaying.isPaused ? "play" : "pause" |
|
](); |
|
if (k === "Escape") dataRef.current.router.close(); |
|
|
|
// captions |
|
if (k === "c") dataRef.current.toggleLastUsed().catch(() => {}); // ignore errors |
|
|
|
// Do a barrell roll! |
|
if (k === "r") { |
|
if (dataRef.current.isRolling || evt.ctrlKey || evt.metaKey) return; |
|
|
|
dataRef.current.setIsRolling(true); |
|
document.querySelector(".popout-location")?.classList.add("roll"); |
|
document.body.setAttribute("data-no-scroll", "true"); |
|
|
|
setTimeout(() => { |
|
document.querySelector(".popout-location")?.classList.remove("roll"); |
|
document.body.removeAttribute("data-no-scroll"); |
|
dataRef.current.setIsRolling(false); |
|
}, 1e3); |
|
} |
|
}; |
|
window.addEventListener("keydown", keyEventHandler); |
|
|
|
return () => { |
|
window.removeEventListener("keydown", keyEventHandler); |
|
}; |
|
}, []); |
|
|
|
return null; |
|
}
|
|
|