From 1e4051f15a802382f139fe001433aaf2f0a2a85a Mon Sep 17 00:00:00 2001 From: highperfocused Date: Sun, 6 Sep 2026 17:56:26 +0200 Subject: [PATCH] fix: guard against double-publish and dropped relay params MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per review: - handlePublish now also checks publish.isPending itself, not just the button's disabled state — a second click landing before React re-renders could otherwise fire mutateAsync twice. - Publishing a draft now merges into the existing params instead of replacing them outright, so relay hints (or anything else already in params) survive the id being added. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01BYiUtZMQeA5RHggQw73wto --- src/apps/notes/Draft.tsx | 5 ++++- src/apps/notes/index.tsx | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/apps/notes/Draft.tsx b/src/apps/notes/Draft.tsx index 4db0494..6481eb7 100644 --- a/src/apps/notes/Draft.tsx +++ b/src/apps/notes/Draft.tsx @@ -27,7 +27,10 @@ export function DraftNote({ onPublished }: { onPublished: (id: string) => void } const trimmed = draft.trim(); const handlePublish = async () => { - if (!trimmed) return; + // Guarded here too, not just via the button's `disabled` — React hasn't + // necessarily re-rendered with publish.isPending yet when a second click + // lands in the same tick, and mutateAsync itself doesn't dedupe calls. + if (!trimmed || publish.isPending) return; try { const event = await publish.mutateAsync({ kind: 1, content: trimmed, tags: [] }); setDraft(''); diff --git a/src/apps/notes/index.tsx b/src/apps/notes/index.tsx index 50457e2..a7f2155 100644 --- a/src/apps/notes/index.tsx +++ b/src/apps/notes/index.tsx @@ -67,7 +67,7 @@ export default function NotesApp({ params, setTitle, setParams }: AppProps) { }, [id, name, setTitle]); if (!id) { - return setParams({ id: publishedId })} />; + return setParams({ ...params, id: publishedId })} />; } if (note.isLoading) {