Files
llink/js/desktop/src/hooks/use-stream-navigation-keys.ts
T
Arjun Patel 095b9876f9 feat: stream list view and tasks (#279)
* first attempt at stream sidebar, tasks, and events

* fix folder from root

* cleanup folders and events, and condense changes

* cleanup and add toggle for sidebar

* cleanup

* fix nits
2026-06-12 12:26:49 -07:00

81 lines
2.1 KiB
TypeScript

import { useEffect, type RefObject } from 'react';
import type { MediaParticleHandle } from '@/features/particles/media-particle-view';
import { isTypingTarget } from '@/lib/keyboard';
const SEEK_DELTA_SEC = 5;
interface UseStreamNavigationKeysOptions {
next: () => void;
prev: () => void;
currentIndex: number;
childrenLength: number;
mediaRef: RefObject<MediaParticleHandle | null>;
onExit: () => void;
onToggleViewMode: () => void;
}
/**
* Arrow keys (with shift+arrow seek), Escape. Skipped while playback is
* paused for any reason (overlay, hold-space, compose).
*/
export function useStreamNavigationKeys({
next,
prev,
currentIndex,
childrenLength,
mediaRef,
onExit,
onToggleViewMode,
}: UseStreamNavigationKeysOptions) {
useEffect(() => {
const onKeyDown = (e: KeyboardEvent) => {
if (isTypingTarget(e)) return;
const hasNext = currentIndex >= 0 && currentIndex < childrenLength - 1;
switch (e.key) {
case 'ArrowRight':
e.preventDefault();
if (!e.shiftKey || !mediaRef.current?.seek(SEEK_DELTA_SEC)) {
if (hasNext) next();
}
break;
case 'ArrowDown':
e.preventDefault();
if (hasNext) next();
break;
case 'ArrowLeft':
e.preventDefault();
if (!e.shiftKey || !mediaRef.current?.seek(-SEEK_DELTA_SEC)) prev();
break;
case 'ArrowUp':
e.preventDefault();
prev();
break;
case 'Escape':
e.preventDefault();
onExit();
break;
case 'l':
case 'L':
// Registered here (not in action keys) because the toggle must work
// in list mode, where playback is suspended and action keys bail.
e.preventDefault();
onToggleViewMode();
break;
}
};
window.addEventListener('keydown', onKeyDown);
return () => window.removeEventListener('keydown', onKeyDown);
}, [
next,
prev,
currentIndex,
childrenLength,
mediaRef,
onExit,
onToggleViewMode,
]);
}