mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-08 22:47:02 +02:00
fix: prevent TipTap editor crash when view is not ready (#188)
The POST command would sometimes crash with "editor view is not available" because code was accessing editor.view.dom before the editor was fully mounted. This fix: - Adds defensive checks for editor.view?.dom in RichEditor's useEffect that attaches keyboard listeners - Makes setContent method check editor view is ready before setting content - Fixes PostViewer draft loading to use retry logic instead of fixed timeout - Removes relayStates from dependency array to prevent effect re-runs - Adds ref to track if draft was already loaded Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -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(() => {
|
||||
|
||||
@@ -564,7 +564,8 @@ export const RichEditor = forwardRef<RichEditorHandle, RichEditorProps>(
|
||||
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<RichEditorHandle, RichEditorProps>(
|
||||
|
||||
// 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<RichEditorHandle, RichEditorProps>(
|
||||
|
||||
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]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user