diff --git a/apps/client/src/widgets/type_widgets/file/Audio.tsx b/apps/client/src/widgets/type_widgets/file/Audio.tsx
index 45c2482215..60db5cf169 100644
--- a/apps/client/src/widgets/type_widgets/file/Audio.tsx
+++ b/apps/client/src/widgets/type_widgets/file/Audio.tsx
@@ -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)}
/>
);
-}
\ No newline at end of file
+}
diff --git a/apps/client/src/widgets/type_widgets/file/MediaPlayer.tsx b/apps/client/src/widgets/type_widgets/file/MediaPlayer.tsx
index 54d76f16c6..7105027a85 100644
--- a/apps/client/src/widgets/type_widgets/file/MediaPlayer.tsx
+++ b/apps/client/src/widgets/type_widgets/file/MediaPlayer.tsx
@@ -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 }) {
+ 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 (
+
+ {formatTime(currentTime)}
+
+ -{formatTime(Math.max(0, duration - currentTime))}
+
+ );
+}
+
+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, playing: boolean }) {
const togglePlayback = () => {
const media = mediaRef.current;
diff --git a/apps/client/src/widgets/type_widgets/file/Video.tsx b/apps/client/src/widgets/type_widgets/file/Video.tsx
index 7c22649d3e..f89aa0c90d 100644
--- a/apps/client/src/widgets/type_widgets/file/Video.tsx
+++ b/apps/client/src/widgets/type_widgets/file/Video.tsx
@@ -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 }) {
/>