mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
tier plans missing
This commit is contained in:
@@ -46,6 +46,8 @@ export function VideoBlock({ content, onUpdate, readOnly = false }) {
|
||||
// blank black box with no indication anything was happening, especially
|
||||
// on large/slow-loading files.
|
||||
const [mediaLoading, setMediaLoading] = useState(true);
|
||||
const [hoverProgress, setHoverProgress] = useState(null); // { x, time }
|
||||
const previewVidRef = useRef(null);
|
||||
|
||||
// Reset player when video changes
|
||||
useEffect(() => {
|
||||
@@ -138,6 +140,36 @@ export function VideoBlock({ content, onUpdate, readOnly = false }) {
|
||||
else el.requestFullscreen?.();
|
||||
};
|
||||
|
||||
// Ignore keystrokes aimed at the seek/volume range inputs so arrow keys
|
||||
// there keep their native behavior instead of double-seeking.
|
||||
const handleKeyDown = useCallback((e) => {
|
||||
if (e.target.tagName === "INPUT") return;
|
||||
const v = vidRef.current;
|
||||
switch (e.key) {
|
||||
case " ":
|
||||
e.preventDefault();
|
||||
togglePlay();
|
||||
break;
|
||||
case "ArrowRight":
|
||||
e.preventDefault();
|
||||
if (v && v.duration) v.currentTime = Math.min(v.currentTime + 5, v.duration);
|
||||
break;
|
||||
case "ArrowLeft":
|
||||
e.preventDefault();
|
||||
if (v) v.currentTime = Math.max(v.currentTime - 5, 0);
|
||||
break;
|
||||
case "m":
|
||||
e.preventDefault();
|
||||
toggleMute();
|
||||
break;
|
||||
case "f":
|
||||
e.preventDefault();
|
||||
toggleFullscreen();
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}, [togglePlay]);
|
||||
|
||||
// #video-block-wrap now wraps both the video area and the controls bar
|
||||
// below it (previously just the video), so fullscreen no longer drops the
|
||||
// seek bar / volume / fullscreen button. isFullscreen also relaxes the
|
||||
@@ -189,7 +221,9 @@ export function VideoBlock({ content, onUpdate, readOnly = false }) {
|
||||
) : src ? (
|
||||
<div
|
||||
id="video-block-wrap"
|
||||
className={`overflow-hidden bg-card ${isFullscreen ? "fixed inset-0 flex flex-col" : "rounded-lg border"}`}
|
||||
tabIndex={0}
|
||||
onKeyDown={handleKeyDown}
|
||||
className={`overflow-hidden bg-card outline-none focus-visible:ring-2 focus-visible:ring-ring ${isFullscreen ? "fixed inset-0 flex flex-col" : "rounded-lg border"}`}
|
||||
>
|
||||
|
||||
{/* ── Video area ── */}
|
||||
@@ -251,7 +285,19 @@ export function VideoBlock({ content, onUpdate, readOnly = false }) {
|
||||
<div className="px-3 pt-2.5 pb-3 flex flex-col gap-2">
|
||||
|
||||
{/* Progress bar */}
|
||||
<div className="relative h-1 bg-border rounded-full cursor-pointer">
|
||||
<div
|
||||
className="relative h-1 bg-border rounded-full cursor-pointer"
|
||||
onMouseMove={(e) => {
|
||||
const rect = e.currentTarget.getBoundingClientRect();
|
||||
const pct = Math.min(Math.max((e.clientX - rect.left) / rect.width, 0), 1);
|
||||
const time = pct * (vidRef.current?.duration ?? 0);
|
||||
setHoverProgress({ x: e.clientX - rect.left, time });
|
||||
if (previewVidRef.current && isFinite(time) && time >= 0) {
|
||||
previewVidRef.current.currentTime = time;
|
||||
}
|
||||
}}
|
||||
onMouseLeave={() => setHoverProgress(null)}
|
||||
>
|
||||
<div
|
||||
className="h-full bg-foreground rounded-full transition-[width] duration-100"
|
||||
style={{ width: `${progress}%` }}
|
||||
@@ -263,6 +309,31 @@ export function VideoBlock({ content, onUpdate, readOnly = false }) {
|
||||
aria-label="Seek"
|
||||
className="absolute inset-0 w-full h-full opacity-0 cursor-pointer"
|
||||
/>
|
||||
|
||||
{/* Scrub preview — hidden video seeked to hover position, no canvas/sprite needed */}
|
||||
{hoverProgress && (
|
||||
<div
|
||||
className="absolute bottom-3 flex flex-col items-center pointer-events-none z-30"
|
||||
style={{ left: `${hoverProgress.x}px`, transform: "translateX(-50%)" }}
|
||||
>
|
||||
<div className="rounded-md overflow-hidden border border-white/20 shadow-xl bg-black" style={{ width: 160, height: 90 }}>
|
||||
<video
|
||||
ref={previewVidRef}
|
||||
src={src}
|
||||
preload="auto"
|
||||
muted
|
||||
playsInline
|
||||
disablePictureInPicture
|
||||
disableRemotePlayback
|
||||
onContextMenu={(e) => e.preventDefault()}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<span className="text-white text-xs mt-1 font-medium tabular-nums drop-shadow bg-black/70 px-1.5 py-0.5 rounded">
|
||||
{fmtTime(hoverProgress.time)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Button row */}
|
||||
|
||||
Reference in New Issue
Block a user