import React from 'react'; import useSWRSubscription from 'swr/subscription'; import type { App, AppGroup, RecordingStatus } from '../types'; import { socket } from '../utils'; import { AppItem } from './app-item'; export function AppList() { const { data: apps = [] } = useSWRSubscription('apps', (_key, { next }) => { let apps: App[] = []; // Initial apps fetch fetch('/api/apps') .then(res => res.json()) .then(data => { apps = data.apps; next(null, apps); }) .catch(err => next(err)); // Subscribe to app updates socket.on('apps:all', data => { next(null, data.apps); apps = data.apps; }); socket.on('apps:state-changed', data => { const index = apps.findIndex(a => a.processId === data.processId); console.log('apps:state-changed', data, index); if (index !== -1) { next( null, apps.toSpliced(index, 1, { ...apps[index], isRunning: data.isRunning, }) ); } }); socket.on('connect', () => { // Refetch on reconnect fetch('/api/apps') .then(res => res.json()) .then(data => next(null, data.apps)) .catch(err => next(err)); }); return () => { socket.off('apps:all'); socket.off('apps:state-changed'); socket.off('connect'); }; }); const { data: recordings = [] } = useSWRSubscription( 'recordings', ( _key: string, { next }: { next: (err: Error | null, data?: RecordingStatus[]) => void } ) => { // Subscribe to recording updates socket.on('apps:recording', (data: { recordings: RecordingStatus[] }) => { next(null, data.recordings); }); return () => { socket.off('apps:recording'); }; } ); const appGroups: AppGroup[] = React.useMemo(() => { const mapping = apps.reduce((acc: Record, app: App) => { if (!acc[app.processGroupId]) { acc[app.processGroupId] = { processGroupId: app.processGroupId, apps: [], rootApp: apps.find((a: App) => a.processId === app.processGroupId) || app, }; } acc[app.processGroupId].apps.push(app); return acc; }, {}); return Object.values(mapping); }, [apps]); const runningApps = (appGroups || []).filter(app => app.apps.some(a => a.isRunning) ); const notRunningApps = (appGroups || []).filter( app => !app.apps.some(a => a.isRunning) ); return (

Active Applications

{runningApps.length} listening
{runningApps.map(app => ( ))} {runningApps.length === 0 && (
No applications are currently listening
)}

Other Applications

{notRunningApps.length} available
{notRunningApps.map(app => ( ))} {notRunningApps.length === 0 && (
No other applications found
)}
); }