Files
gerbeur/src/components/GlobalPlayer.tsx
2026-06-27 19:44:35 +00:00

122 lines
3.4 KiB
TypeScript

import { useContext, useEffect, useRef, useState } from "react";
import { PlayerContext } from "../contexts/PlayerContext.ts";
import { MediaPlayer } from "./MediaPlayer.tsx";
function itemKey(
item: { kind: string; embedUrl?: string; fileUrl?: string } | null,
) {
if (!item) return null;
return item.kind === "embed" ? item.embedUrl : item.fileUrl;
}
export function GlobalPlayer() {
const {
current,
startTime,
autoplay,
stop,
seekRef,
toggleRef,
onPlayStateChange,
onTimeUpdate,
} = useContext(PlayerContext);
const ref = useRef<HTMLDivElement>(null);
const [reduced, setReduced] = useState(false);
const [prevKey, setPrevKey] = useState(itemKey(current));
if (prevKey !== itemKey(current)) {
setPrevKey(itemKey(current));
if (current) setReduced(false);
}
useEffect(() => {
if (!current) {
document.body.classList.remove("has-player");
document.body.style.removeProperty("--player-height");
return;
}
const el = ref.current;
if (!el) return;
document.body.style.setProperty("--player-height", `${el.offsetHeight}px`);
document.body.classList.add("has-player");
const observer = new ResizeObserver(() => {
document.body.style.setProperty(
"--player-height",
`${el.offsetHeight}px`,
);
});
observer.observe(el);
return () => {
observer.disconnect();
document.body.classList.remove("has-player");
document.body.style.removeProperty("--player-height");
};
}, [current]);
if (!current) return null;
const typeClass = current.kind === "embed"
? current.type
: current.mimeType.startsWith("video/")
? "file-video"
: "file-audio";
const title = current.title ??
(current.kind === "embed" ? current.embedUrl : current.fileUrl);
return (
<div
className={`global-player global-player--${typeClass}${
reduced ? " global-player--reduced" : ""
}`}
ref={ref}
>
<div className="global-player-header">
<span className="global-player-title">{title}</span>
<button
type="button"
className="btn btn--ghost"
onClick={() => setReduced((r) => !r)}
>
{reduced ? "▲" : "▼"}
</button>
<button type="button" className="btn btn--ghost" onClick={stop}>
</button>
</div>
<div className="global-player-body">
{current.kind === "embed"
? (
<div className="global-player-iframe-wrap">
<iframe
src={current.embedUrl}
className={`global-player-iframe--${current.type}`}
allow="autoplay; encrypted-media"
allowFullScreen
/>
</div>
)
: (
<div className="global-player-media-wrap">
<MediaPlayer
key={current.fileUrl}
src={current.fileUrl}
kind={current.mimeType.startsWith("video/") ? "video" : "audio"}
mime={current.mimeType}
autoplay={autoplay}
startTime={startTime}
onPlayStateChange={onPlayStateChange}
onTimeUpdate={onTimeUpdate}
seekRef={seekRef}
toggleRef={toggleRef}
/>
</div>
)}
</div>
</div>
);
}