feat(audio): reintroduce seek bar

This commit is contained in:
Elian Doran 2026-03-11 19:03:09 +02:00
parent a5a345728c
commit 92f0144b48
No known key found for this signature in database
3 changed files with 59 additions and 54 deletions

View File

@ -2,7 +2,7 @@ import { useRef, useState } from "preact/hooks";
import FNote from "../../../entities/fnote";
import { getUrlForDownload } from "../../../services/open";
import { PlayPauseButton } from "./MediaPlayer";
import { PlayPauseButton, SeekBar } from "./MediaPlayer";
export default function AudioPreview({ note }: { note: FNote }) {
const [playing, setPlaying] = useState(false);
@ -18,10 +18,14 @@ export default function AudioPreview({ note }: { note: FNote }) {
onPause={() => setPlaying(false)}
/>
<div className="video-preview-controls">
<div className="center">
<PlayPauseButton mediaRef={audioRef} playing={playing} />
<SeekBar mediaRef={audioRef} />
<div class="video-buttons-row">
<div className="center">
<PlayPauseButton mediaRef={audioRef} playing={playing} />
</div>
</div>
</div>
</div>
);
}
}

View File

@ -1,8 +1,57 @@
import { RefObject } from "preact";
import { useEffect, useState } from "preact/hooks";
import { t } from "../../../services/i18n";
import ActionButton from "../../react/ActionButton";
export function SeekBar({ mediaRef }: { mediaRef: RefObject<HTMLVideoElement | HTMLAudioElement> }) {
const [currentTime, setCurrentTime] = useState(0);
const [duration, setDuration] = useState(0);
useEffect(() => {
const media = mediaRef.current;
if (!media) return;
const onTimeUpdate = () => setCurrentTime(media.currentTime);
const onDurationChange = () => setDuration(media.duration);
media.addEventListener("timeupdate", onTimeUpdate);
media.addEventListener("durationchange", onDurationChange);
return () => {
media.removeEventListener("timeupdate", onTimeUpdate);
media.removeEventListener("durationchange", onDurationChange);
};
}, []);
const onSeek = (e: Event) => {
const media = mediaRef.current;
if (!media) return;
media.currentTime = parseFloat((e.target as HTMLInputElement).value);
};
return (
<div class="video-seekbar-row">
<span class="video-time">{formatTime(currentTime)}</span>
<input
type="range"
class="video-trackbar"
min={0}
max={duration || 0}
step={0.1}
value={currentTime}
onInput={onSeek}
/>
<span class="video-time">-{formatTime(Math.max(0, duration - currentTime))}</span>
</div>
);
}
function formatTime(seconds: number): string {
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${mins}:${secs.toString().padStart(2, "0")}`;
}
export function PlayPauseButton({ mediaRef, playing }: { mediaRef: RefObject<HTMLVideoElement | HTMLAudioElement>, playing: boolean }) {
const togglePlayback = () => {
const media = mediaRef.current;

View File

@ -10,13 +10,7 @@ import ActionButton from "../../react/ActionButton";
import Dropdown from "../../react/Dropdown";
import Icon from "../../react/Icon";
import NoItems from "../../react/NoItems";
import { PlayPauseButton } from "./MediaPlayer";
function formatTime(seconds: number): string {
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${mins}:${secs.toString().padStart(2, "0")}`;
}
import { PlayPauseButton, SeekBar } from "./MediaPlayer";
const AUTO_HIDE_DELAY = 3000;
@ -120,7 +114,7 @@ export default function VideoPreview({ note }: { note: FNote }) {
/>
<div className="video-preview-controls">
<SeekBar videoRef={videoRef} />
<SeekBar mediaRef={videoRef} />
<div class="video-buttons-row">
<div className="left">
<PlaybackSpeed videoRef={videoRef} />
@ -187,48 +181,6 @@ function SkipButton({ videoRef, seconds, icon, text }: { videoRef: RefObject<HTM
);
}
function SeekBar({ videoRef }: { videoRef: RefObject<HTMLVideoElement> }) {
const [currentTime, setCurrentTime] = useState(0);
const [duration, setDuration] = useState(0);
useEffect(() => {
const video = videoRef.current;
if (!video) return;
const onTimeUpdate = () => setCurrentTime(video.currentTime);
const onDurationChange = () => setDuration(video.duration);
video.addEventListener("timeupdate", onTimeUpdate);
video.addEventListener("durationchange", onDurationChange);
return () => {
video.removeEventListener("timeupdate", onTimeUpdate);
video.removeEventListener("durationchange", onDurationChange);
};
}, []);
const onSeek = (e: Event) => {
const video = videoRef.current;
if (!video) return;
video.currentTime = parseFloat((e.target as HTMLInputElement).value);
};
return (
<div class="video-seekbar-row">
<span class="video-time">{formatTime(currentTime)}</span>
<input
type="range"
class="video-trackbar"
min={0}
max={duration || 0}
step={0.1}
value={currentTime}
onInput={onSeek}
/>
<span class="video-time">-{formatTime(Math.max(0, duration - currentTime))}</span>
</div>
);
}
function VolumeControl({ videoRef }: { videoRef: RefObject<HTMLVideoElement> }) {
const [volume, setVolume] = useState(() => videoRef.current?.volume ?? 1);
const [muted, setMuted] = useState(() => videoRef.current?.muted ?? false);