A simple, clean, fast browser for the AtmosphereConf(2026) VODs
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: restore stream nav clicks and direct-link video playback

j4ckxyz 8777bc69 669df404

+13 -7
+3 -3
src/components/layout/app-shell.tsx
··· 107 107 <div className="relative isolate min-h-svh bg-bg"> 108 108 <header 109 109 className={cn( 110 - 'sticky top-0 z-10 border-b border-line/45 bg-surface/80 transition-transform duration-300 ease-out supports-[backdrop-filter]:backdrop-blur-md', 110 + 'sticky top-0 z-40 border-b border-line/45 bg-surface/80 transition-transform duration-300 ease-out supports-[backdrop-filter]:backdrop-blur-md', 111 111 isHeaderHidden ? '-translate-y-full' : 'translate-y-0', 112 112 )} 113 113 onFocusCapture={() => setIsHeaderHidden(false)} ··· 129 129 130 130 <main 131 131 id="main-content" 132 - className="relative z-10 mx-auto w-full max-w-5xl px-3 pb-24 pt-7 sm:px-4 md:px-6 md:pb-10 md:pt-10" 132 + className="relative z-0 mx-auto w-full max-w-5xl px-3 pb-24 pt-7 sm:px-4 md:px-6 md:pb-10 md:pt-10" 133 133 > 134 134 {children} 135 135 </main> ··· 178 178 </footer> 179 179 180 180 <nav 181 - className="fixed inset-x-2 bottom-[max(0.5rem,env(safe-area-inset-bottom))] z-20 flex min-h-16 items-center gap-1.5 rounded-xl border border-line/45 bg-surface/80 px-1.5 py-1.5 supports-[backdrop-filter]:backdrop-blur-md sm:inset-x-3 sm:gap-2 sm:px-2 sm:py-2 md:hidden" 181 + className="fixed inset-x-2 bottom-[max(0.5rem,env(safe-area-inset-bottom))] z-50 flex min-h-16 items-center gap-1.5 rounded-xl border border-line/45 bg-surface/80 px-1.5 py-1.5 supports-[backdrop-filter]:backdrop-blur-md sm:inset-x-3 sm:gap-2 sm:px-2 sm:py-2 md:hidden" 182 182 aria-label="Bottom tabs" 183 183 > 184 184 {navItems.map((item) => (
+10 -4
src/pages/video-page.tsx
··· 46 46 const hlsRef = useRef<HlsLike | null>(null) 47 47 const playerContainerRef = useRef<HTMLDivElement | null>(null) 48 48 49 + const [videoElement, setVideoElement] = useState<HTMLVideoElement | null>(null) 49 50 const [status, setStatus] = useState<PlaybackStatus>('idle') 50 51 const [error, setError] = useState<string | null>(null) 51 52 const [reloadToken, setReloadToken] = useState(0) ··· 53 54 const [resolvedTalk, setResolvedTalk] = useState<AppTalk | null>(null) 54 55 const [metadataLoading, setMetadataLoading] = useState<boolean>(false) 55 56 const [ionosphere, setIonosphere] = useState<IonosphereEnrichment | null>(null) 57 + 58 + const handleVideoRef = useCallback((node: HTMLVideoElement | null) => { 59 + videoRef.current = node 60 + setVideoElement(node) 61 + }, []) 56 62 57 63 const resolvedUri = useMemo( 58 64 () => (didParam && rkeyParam ? toVideoUriFromParams(didParam, rkeyParam) : undefined), ··· 110 116 }, [resolvedUri, talks, talksLoading]) 111 117 112 118 useEffect(() => { 113 - if (!resolvedUri || !videoRef.current) { 119 + if (!resolvedUri || !videoElement) { 114 120 return 115 121 } 116 122 117 123 const uri = resolvedUri 118 - const video = videoRef.current 124 + const video = videoElement 119 125 setStatus('loading') 120 126 setError(null) 121 127 setPlaylistUrl(null) ··· 200 206 video.load() 201 207 setPlaylistUrl(null) 202 208 } 203 - }, [resolvedUri, reloadToken]) 209 + }, [resolvedUri, reloadToken, videoElement]) 204 210 205 211 useEffect(() => { 206 212 if (!talk || !isAtmosphereTalk(talk)) { ··· 412 418 <section className="space-y-5" ref={playerContainerRef}> 413 419 <div className="relative overflow-hidden rounded-xl border border-line/45 bg-surface/80"> 414 420 <video 415 - ref={videoRef} 421 + ref={handleVideoRef} 416 422 className="aspect-video w-full" 417 423 controls 418 424 playsInline