// Copyright 2023 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React, { useCallback, useRef } from 'react'; import { useRefMerger } from '../../hooks/useRefMerger'; import type { LocalizerType } from '../../types/Util'; import { durationToPlaybackText } from '../../util/durationToPlaybackText'; import { Waveform } from './Waveform'; import { arrow } from '../../util/keyboard'; type Props = Readonly<{ i18n: LocalizerType; peaks: ReadonlyArray; currentTime: number; duration: number | undefined; barMinHeight: number; barMaxHeight: number; onClick: (positionAsRatio: number) => void; onScrub: (positionAsRatio: number) => void; }>; const BAR_COUNT = 47; const REWIND_BAR_COUNT = 2; // Increments for keyboard audio seek (in seconds)\ const SMALL_INCREMENT = 1; const BIG_INCREMENT = 5; export const WaveformScrubber = React.forwardRef(function WaveformScrubber( { i18n, peaks, barMinHeight, barMaxHeight, currentTime, duration, onClick, onScrub, }: Props, ref ): JSX.Element { const refMerger = useRefMerger(); const waveformRef = useRef(null); // Clicking waveform moves playback head position and starts playback. const handleClick = useCallback( (event: React.MouseEvent) => { event.preventDefault(); event.stopPropagation(); if (!waveformRef.current) { return; } const boundingRect = waveformRef.current.getBoundingClientRect(); let progress = (event.pageX - boundingRect.left) / boundingRect.width; if (progress <= REWIND_BAR_COUNT / BAR_COUNT) { progress = 0; } onClick(progress); }, [waveformRef, onClick] ); // Keyboard navigation for waveform. Pressing keys moves playback head // forward/backwards. const handleKeyDown = (event: React.KeyboardEvent) => { if (!duration) { return; } let increment: number; if (event.key === 'ArrowUp' || event.key === arrow('end')) { increment = +SMALL_INCREMENT; } else if (event.key === 'ArrowDown' || event.key === arrow('start')) { increment = -SMALL_INCREMENT; } else if (event.key === 'PageUp') { increment = +BIG_INCREMENT; } else if (event.key === 'PageDown') { increment = -BIG_INCREMENT; } else { // We don't handle other keys return; } event.preventDefault(); event.stopPropagation(); const currentPosition = currentTime / duration; const positionIncrement = increment / duration; const newPosition = currentPosition + positionIncrement; onScrub(newPosition); }; return (
); });