v2: global player, infinite scroll, image picker, threaded comments

This commit is contained in:
khannurien
2026-03-21 13:55:22 +00:00
parent be426eb150
commit 7c098e7c4c
48 changed files with 4346 additions and 711 deletions

View File

@@ -0,0 +1,62 @@
import { useContext, useEffect, useRef, useState } from "react";
import { PlayerContext } from "../contexts/PlayerContext.ts";
export function GlobalPlayer() {
const { current, stop } = useContext(PlayerContext);
const ref = useRef<HTMLDivElement>(null);
const [reduced, setReduced] = useState(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]);
useEffect(() => {
if (current) setReduced(false);
}, [current?.embedUrl]);
if (!current) return null;
return (
<div className={`global-player global-player--${current.type}${reduced ? " global-player--reduced" : ""}`} ref={ref}>
<div className="global-player-header">
<span className="global-player-title">{current.title ?? current.embedUrl}</span>
<button className="btn btn--ghost" onClick={() => setReduced((r) => !r)}>
{reduced ? "▲" : "▼"}
</button>
<button className="btn btn--ghost" onClick={stop}>
</button>
</div>
<div className="global-player-body">
<div className="global-player-iframe-wrap">
<iframe
src={current.embedUrl}
className={`global-player-iframe--${current.type}`}
allow="autoplay; encrypted-media"
allowFullScreen
/>
</div>
</div>
</div>
);
}