feat: auto-exit stream on playback completion

This commit is contained in:
talksik
2026-03-19 12:34:20 -07:00
parent 122d8fed73
commit 5a78cbae08
2 changed files with 76 additions and 0 deletions
+54
View File
@@ -90,6 +90,49 @@ const initialState: PlaybackState = {
paused: false,
};
// --- Exit countdown hook ---
const EXIT_DELAY_MS = 5000;
const EXIT_TICK_MS = 100;
function useExitCountdown(
status: PlaybackStatus,
composeActive: boolean,
onExit: () => void,
) {
const [exitProgress, setExitProgress] = useState<number | null>(null);
// Start/cancel countdown based on playback status
useEffect(() => {
if (status === "ended") {
setExitProgress(0);
} else {
setExitProgress(null);
}
}, [status]);
// Tick the countdown forward (pauses when compose is active)
useEffect(() => {
if (exitProgress === null || composeActive) return;
const interval = setInterval(() => {
setExitProgress((prev) => {
if (prev === null) return null;
const next = prev + EXIT_TICK_MS / EXIT_DELAY_MS;
if (next >= 1) {
onExit();
return 1;
}
return next;
});
}, EXIT_TICK_MS);
return () => clearInterval(interval);
}, [exitProgress !== null, composeActive, onExit]);
return exitProgress;
}
// --- StreamView ---
interface StreamViewProps {
@@ -107,6 +150,16 @@ export function StreamView({ path, streamParticle }: StreamViewProps) {
const [progress, setProgress] = useState(0);
const hasInitializedRef = useRef<string | null>(null);
const handleExitNavigate = useCallback(() => {
navigate(`/${networkId}`);
}, [navigate, networkId]);
const exitProgress = useExitCountdown(
state.status,
composeActive,
handleExitNavigate,
);
const userId = useAuthStore((s) => s.user?.id);
// Reset progress when particle changes
@@ -276,6 +329,7 @@ export function StreamView({ path, streamParticle }: StreamViewProps) {
current={state.currentIndex}
progress={progress}
onGoTo={goTo}
exitProgress={exitProgress}
/>
</div>
@@ -5,6 +5,7 @@ interface PlaybackPageIndicatorProps {
current: number;
progress: number;
onGoTo: (index: number) => void;
exitProgress?: number | null;
}
export function PlaybackPageIndicator({
@@ -12,6 +13,7 @@ export function PlaybackPageIndicator({
current,
progress,
onGoTo,
exitProgress,
}: PlaybackPageIndicatorProps) {
if (total === 0) return null;
@@ -51,6 +53,26 @@ export function PlaybackPageIndicator({
/>
</button>
))}
{/* Exit countdown segment */}
{exitProgress != null && (
<div className="relative h-3 flex-1">
<div
className={cn(
"absolute inset-x-0 top-1 h-1 rounded-full bg-white/30",
)}
/>
<div
className={cn(
"absolute left-0 top-1 h-1 rounded-full bg-white/90",
)}
style={{
width: `${exitProgress * 100}%`,
transition: "width 150ms linear",
}}
/>
</div>
)}
</div>
);
}