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. */}
+