diff --git a/src/components/PostViewer.tsx b/src/components/PostViewer.tsx index 6779888..0d12741 100644 --- a/src/components/PostViewer.tsx +++ b/src/components/PostViewer.tsx @@ -142,9 +142,12 @@ export function PostViewer({ windowId }: PostViewerProps = {}) { } }, [writeRelays, updateRelayStates]); + // Track if draft has been loaded to prevent re-runs + const draftLoadedRef = useRef(false); + // Load draft from localStorage on mount useEffect(() => { - if (!pubkey) return; + if (!pubkey || draftLoadedRef.current) return; const draftKey = windowId ? `${DRAFT_STORAGE_KEY}-${pubkey}-${windowId}` @@ -154,15 +157,20 @@ export function PostViewer({ windowId }: PostViewerProps = {}) { if (savedDraft) { try { const draft = JSON.parse(savedDraft); + draftLoadedRef.current = true; - // Restore editor content - if (editorRef.current && draft.editorState) { - // Use setTimeout to ensure editor is fully mounted - setTimeout(() => { - if (editorRef.current && draft.editorState) { + // Restore editor content with retry logic for editor readiness + if (draft.editorState) { + const trySetContent = (attempts = 0) => { + if (editorRef.current) { editorRef.current.setContent(draft.editorState); + } else if (attempts < 10) { + // Retry up to 10 times with 50ms intervals (500ms total) + setTimeout(() => trySetContent(attempts + 1), 50); } - }, 100); + }; + // Start trying after a short delay to let editor mount + setTimeout(() => trySetContent(), 50); } // Restore selected relays @@ -172,22 +180,24 @@ export function PostViewer({ windowId }: PostViewerProps = {}) { // Restore added relays (relays not in writeRelays) if (draft.addedRelays && Array.isArray(draft.addedRelays)) { - const currentRelayUrls = new Set(relayStates.map((r) => r.url)); - const newRelays = draft.addedRelays - .filter((url: string) => !currentRelayUrls.has(url)) - .map((url: string) => ({ - url, - status: "pending" as RelayStatus, - })); - if (newRelays.length > 0) { - setRelayStates((prev) => [...prev, ...newRelays]); - } + setRelayStates((prev) => { + const currentRelayUrls = new Set(prev.map((r) => r.url)); + const newRelays = draft.addedRelays + .filter((url: string) => !currentRelayUrls.has(url)) + .map((url: string) => ({ + url, + status: "pending" as RelayStatus, + })); + return newRelays.length > 0 ? [...prev, ...newRelays] : prev; + }); } } catch (err) { console.error("Failed to load draft:", err); } + } else { + draftLoadedRef.current = true; } - }, [pubkey, windowId, relayStates]); + }, [pubkey, windowId]); // Save draft to localStorage on content change const saveDraft = useCallback(() => { diff --git a/src/components/editor/RichEditor.tsx b/src/components/editor/RichEditor.tsx index e315d2d..947c911 100644 --- a/src/components/editor/RichEditor.tsx +++ b/src/components/editor/RichEditor.tsx @@ -564,7 +564,8 @@ export const RichEditor = forwardRef( return editor?.getJSON() || null; }, setContent: (json: any) => { - if (editor && json) { + // Check editor and view are ready before setting content + if (editor?.view?.dom && json) { editor.commands.setContent(json); } }, @@ -574,7 +575,8 @@ export const RichEditor = forwardRef( // Handle submit on Ctrl/Cmd+Enter useEffect(() => { - if (!editor) return; + // Check both editor and editor.view exist (view may not be ready immediately) + if (!editor?.view?.dom) return; const handleKeyDown = (event: KeyboardEvent) => { if ((event.ctrlKey || event.metaKey) && event.key === "Enter") { @@ -585,7 +587,8 @@ export const RichEditor = forwardRef( editor.view.dom.addEventListener("keydown", handleKeyDown); return () => { - editor.view.dom.removeEventListener("keydown", handleKeyDown); + // Also check view.dom exists in cleanup (editor might be destroyed) + editor.view?.dom?.removeEventListener("keydown", handleKeyDown); }; }, [editor, handleSubmit]);