All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 44s
122 lines
3.4 KiB
TypeScript
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>
|
|
);
|
|
}
|