import type { HocuspocusProvider } from '@hocuspocus/provider'; import { useEffect, useState } from 'react'; import type { CaretUser } from './collaboration-caret'; export type Participant = CaretUser; /** * The current participants on a page, derived from the collab awareness states * (issue #37). Deduplicated by user id (a user with two tabs is one avatar) and * updated whenever awareness changes, so a disconnect drops the participant * within seconds. */ export function usePresence(provider: HocuspocusProvider | null): Participant[] { const [participants, setParticipants] = useState([]); useEffect(() => { const awareness = provider?.awareness; if (!awareness) { setParticipants([]); return; } const update = (): void => { const byUser = new Map(); for (const state of awareness.getStates().values()) { const user = (state as { user?: Partial }).user; if (!user?.userId || byUser.has(user.userId)) continue; byUser.set(user.userId, { userId: user.userId, name: user.name ?? '', color: user.color ?? '#495057', readOnly: Boolean(user.readOnly), }); } setParticipants([...byUser.values()]); }; update(); awareness.on('change', update); return () => awareness.off('change', update); }, [provider]); return participants; }