import { useCallback, useState } from 'react'; import { createPortal } from 'react-dom'; import { toast } from 'sonner'; import type { Particle } from '@/api/types'; import { particlePath, parseParticlePath, toFirestoreDocPath, type ParticlePath, } from '@/lib/particle-path'; import { editTextParticleContent } from '@/lib/firestore-particles'; import { TextEditor } from '@/features/compose/text-editor'; import { useSuspendPlayback } from '@/hooks/use-suspend-playback'; type TextParticle = Extract; interface TextEditOverlayProps { particle: TextParticle; streamPath: ParticlePath; onClose: () => void; } export function TextEditOverlay({ particle, streamPath, onClose, }: TextEditOverlayProps) { useSuspendPlayback(true, 'text-edit'); const [textContent, setTextContent] = useState(particle.properties.content); const [saving, setSaving] = useState(false); const handleSubmit = useCallback(async () => { if (saving) return; const trimmed = textContent.trim(); if (!trimmed) return; if (trimmed === particle.properties.content) { onClose(); return; } setSaving(true); try { const { networkId, segments } = parseParticlePath(streamPath); const docPath = toFirestoreDocPath( particlePath(networkId, [...segments, particle.id]), ); await editTextParticleContent(docPath, trimmed); onClose(); } catch (err) { toast.error(err instanceof Error ? err.message : 'Failed to save'); setSaving(false); } }, [ saving, textContent, particle.properties.content, particle.id, streamPath, onClose, ]); return createPortal( // React synthetic events bubble through the React tree (not the DOM tree), // so clicks here would reach stream-view's click-to-navigate handler even // though we're portaled to document.body. Stop propagation at the root.
e.stopPropagation()}>
, document.body, ); }