fix: resolve 3 real bugs found in PR review

- ConfigEditorDialog: wrap api.config.get() in try/catch to prevent
  dialog from getting stuck in loading state on IPC failure
- CreateTaskDialog: clear description and chips when dialog opens
  without defaults, preventing stale state from previous opens
- useAttachments: handle persistenceKey→undefined transition by
  flushing pending saves and clearing stale attachment state
This commit is contained in:
iliya 2026-03-05 21:49:53 +02:00
parent 64f2d3f413
commit 4cc297690a
3 changed files with 64 additions and 47 deletions

View file

@ -175,57 +175,63 @@ export const ConfigEditorDialog = ({
setJsonError(null); setJsonError(null);
const init = async (): Promise<void> => { const init = async (): Promise<void> => {
const config = await api.config.get(); try {
if (destroyed) return; const config = await api.config.get();
if (destroyed) return;
const jsonText = JSON.stringify(config, null, 2); const jsonText = JSON.stringify(config, null, 2);
initialConfigRef.current = jsonText; initialConfigRef.current = jsonText;
setLoading(false); setLoading(false);
// Wait for DOM render // Wait for DOM render
requestAnimationFrame(() => { requestAnimationFrame(() => {
if (destroyed || !editorRef.current) return; if (destroyed || !editorRef.current) return;
// Clean up existing view // Clean up existing view
if (viewRef.current) { if (viewRef.current) {
viewRef.current.destroy(); viewRef.current.destroy();
viewRef.current = null; viewRef.current = null;
} }
const state = EditorState.create({ const state = EditorState.create({
doc: jsonText, doc: jsonText,
extensions: [ extensions: [
lineNumbers(), lineNumbers(),
highlightActiveLineGutter(), highlightActiveLineGutter(),
highlightActiveLine(), highlightActiveLine(),
history(), history(),
foldGutter(), foldGutter(),
indentOnInput(), indentOnInput(),
bracketMatching(), bracketMatching(),
json(), json(),
syntaxHighlighting(oneDarkHighlightStyle), syntaxHighlighting(oneDarkHighlightStyle),
jsonLinter, jsonLinter,
lintGutter(), lintGutter(),
search(), search(),
keymap.of([...defaultKeymap, ...historyKeymap, ...foldKeymap, ...searchKeymap]), keymap.of([...defaultKeymap, ...historyKeymap, ...foldKeymap, ...searchKeymap]),
baseEditorTheme, baseEditorTheme,
configEditorTheme, configEditorTheme,
// eslint-disable-next-line sonarjs/no-nested-functions -- CodeMirror listener callback within useEffect setup // eslint-disable-next-line sonarjs/no-nested-functions -- CodeMirror listener callback within useEffect setup
EditorView.updateListener.of((update) => { EditorView.updateListener.of((update) => {
if (update.docChanged) { if (update.docChanged) {
const text = update.state.doc.toString(); const text = update.state.doc.toString();
scheduleSave(text); scheduleSave(text);
} }
}), }),
], ],
});
const view = new EditorView({
state,
parent: editorRef.current,
});
viewRef.current = view;
}); });
} catch (e) {
const view = new EditorView({ if (destroyed) return;
state, setLoading(false);
parent: editorRef.current, setJsonError(e instanceof Error ? e.message : 'Failed to load config');
}); }
viewRef.current = view;
});
}; };
void init(); void init();

View file

@ -102,6 +102,10 @@ export const CreateTaskDialog = ({
descChipDraft.setChips([defaultChip]); descChipDraft.setChips([defaultChip]);
} else if (defaultDescription) { } else if (defaultDescription) {
descriptionDraft.setValue(defaultDescription); descriptionDraft.setValue(defaultDescription);
descChipDraft.clearChipDraft();
} else {
descriptionDraft.clearDraft();
descChipDraft.clearChipDraft();
} }
setOwner(defaultOwner); setOwner(defaultOwner);
setBlockedBy([]); setBlockedBy([]);

View file

@ -106,7 +106,14 @@ export function useAttachments(options?: UseAttachmentsOptions): UseAttachmentsR
// Load persisted attachments on mount // Load persisted attachments on mount
useEffect(() => { useEffect(() => {
if (!persistenceKey) return; if (!persistenceKey) {
// Transitioning to non-persistent context: flush pending save and clear stale state
flushPending();
attachmentsRef.current = [];
// eslint-disable-next-line react-hooks/set-state-in-effect -- intentional sync reset on key transition
setAttachments([]);
return;
}
let cancelled = false; let cancelled = false;
// Flush any pending debounced save for the previous key before switching. // Flush any pending debounced save for the previous key before switching.