feat(messaging): sync draft emails and edit them in the thread composer (#22178)

Stop excluding drafts from sync across all three providers (Gmail DRAFT
label, Microsoft/IMAP Drafts folder) and add an isDraft boolean field on
Message so drafts are queryable by the API and AI agents.

Drafts render in the thread with a Draft tag; clicking one opens the
existing reply composer pre-filled with the draft's recipients, subject
and body, and Send reuses the existing send-email flow.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22178?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
neo773
2026-06-30 16:37:53 +05:30
committed by GitHub
parent c4a6446757
commit 9f3ebaaf22
60 changed files with 2057 additions and 1100 deletions
@@ -415,4 +415,24 @@ export const buildMessageStandardFlatFieldMetadatas = ({
twentyStandardApplicationId,
now,
}),
isDraft: createStandardFieldFlatMetadata({
objectName,
workspaceId,
context: {
fieldName: 'isDraft',
type: FieldMetadataType.BOOLEAN,
label: i18nLabel(msg`Is draft`),
description: i18nLabel(
msg`Whether this message is an unsent draft synced from the provider`,
),
icon: 'IconPencil',
isNullable: false,
isUIEditable: false,
defaultValue: false,
},
standardObjectMetadataRelatedEntityIds,
dependencyFlatEntityMaps,
twentyStandardApplicationId,
now,
}),
});