diff --git a/artifacts/tx-os/src/pages/notes.tsx b/artifacts/tx-os/src/pages/notes.tsx index 51972719..ff27c926 100644 --- a/artifacts/tx-os/src/pages/notes.tsx +++ b/artifacts/tx-os/src/pages/notes.tsx @@ -479,7 +479,11 @@ export default function NotesPage() { )} - + setSendForNoteId(id)} + /> {loadingNotes ? ( ) : filteredNotes.length === 0 ? ( @@ -2451,9 +2455,11 @@ function LabelMenu({ function Composer({ labels, folderSelection, + onSend, }: { labels: NoteLabel[]; folderSelection: FolderSelection; + onSend: (id: number) => void; }) { const { t } = useTranslation(); const [open, setOpen] = useState(false); @@ -2508,7 +2514,12 @@ function Composer({ setOpen(false); }; - const save = () => { + // Build the create-note payload from current composer state and a + // boolean indicating whether the composer is empty (no title and no + // body / checklist items). Shared by save() and sendNew() so the + // "save before send" path uses the exact same data the bare Save + // button would have submitted. + const buildPayload = () => { const cleanItems = kind === "checklist" ? items @@ -2518,12 +2529,9 @@ function Composer({ const isEmpty = !title.trim() && (kind === "checklist" ? cleanItems.length === 0 : !content.trim()); - if (isEmpty) { - reset(); - return; - } - create.mutate( - { + return { + isEmpty, + payload: { title: title.trim(), content: kind === "checklist" ? "" : content.trim(), color, @@ -2532,10 +2540,42 @@ function Composer({ items: kind === "checklist" ? cleanItems : null, folderId: targetFolderId, }, - { onSuccess: reset }, - ); + }; }; + const save = () => { + const { isEmpty, payload } = buildPayload(); + if (isEmpty) { + reset(); + return; + } + create.mutate(payload, { onSuccess: reset }); + }; + + // Task #444: Send button inside the composer. The plan agreed with + // the user is "save first, then open the send dialog for the new + // note". On success we reset the composer (mirroring save()) and + // hand the freshly created note's id up to the page so it can mount + // . On failure we leave the composer state intact + // so the user can retry — same as the existing save error behavior. + const sendNew = () => { + const { isEmpty, payload } = buildPayload(); + if (isEmpty || create.isPending) return; + create.mutate(payload, { + onSuccess: (created) => { + reset(); + onSend(created.id); + }, + }); + }; + + const sendDisabled = + create.isPending || + (!title.trim() && + (kind === "checklist" + ? items.every((it) => !it.text.trim()) + : !content.trim())); + return (
-
+
+ {/* Task #444: Send action inside the composer. Saves first + (auto-save on send was explicitly OK'd by the user), + then opens for the new note. Disabled + while empty or while the create request is in flight to + prevent duplicate notes from rapid clicks. */} +