import { useEffect } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { useQueryClient } from '@tanstack/react-query'; import { Lock } from 'lucide-react'; import { isParticleDeleted, type Particle } from '@/api/types'; import { useLiveParticle } from '@/hooks/use-particle'; import { particlePath, type ParticlePath } from '@/lib/particle-path'; import { Button } from '@/components/ui/button'; import Layout from '@/features/layout'; import { StreamView } from '@/features/particles/stream-view'; import { FolderView } from '@/features/particles/folder-view'; import { MediaParticleView } from '@/features/particles/media-particle-view'; import { TextParticleView } from '@/features/particles/text-particle-view'; import { TaskParticleView } from '@/features/particles/task-particle-view'; import { FallbackParticleView } from '@/features/particles/fallback-particle-view'; import { DeletedParticleView } from '@/features/particles/deleted-particle-view'; /** * Route-level component for /:networkId/*. * Reads params from the router, resolves the particle, and renders * the appropriate view based on particle type. */ export default function ParticleViewResolver() { const { networkId, '*': rest } = useParams(); if (!networkId) throw new Error('ParticleViewResolver requires a :networkId route param'); const segments = (rest ?? '').split('/').filter(Boolean); const path = particlePath(networkId, segments); // path of current container particle const { particle, isLoading, error } = useLiveParticle(path); if (isLoading) { return (

Loading...

); } if (error || !particle) { // Errors here are almost always Firestore permission-denied — the user lost // access to the network or to a custom-visibility particle. The React Router // stays on the dead route, so without an explicit escape the user is stuck. return ( ); } // Streams render their own full-screen chrome; folders and leaves live // inside the app Layout (breadcrumbs, full-height column) like the root. switch (particle.type) { case 'stream': return ; case 'folder': return ( ); default: return ( ); } } const noop = () => {}; /** * Standalone view for a leaf particle opened directly (e.g. from a folder), * outside any stream playback: renders the particle's native view with no * auto-advance. */ function LeafParticleView({ particle, containerPath, networkId, }: { particle: Particle; containerPath: ParticlePath; networkId: string; }) { const content = (() => { if (isParticleDeleted(particle)) { return ( ); } switch (particle.type) { case 'media': return ( ); case 'text': return ( ); case 'task': return ( ); default: return ( ); } })(); return (
{content}
); } function InaccessibleParticle() { const navigate = useNavigate(); const queryClient = useQueryClient(); useEffect(() => { // Refresh the networks list so the home page reflects current access. queryClient.invalidateQueries({ queryKey: ['networks'] }); }, [queryClient]); return (

This particle isn't available

It may have been deleted, or your access was removed.

); }