chore: remove noisy comments, keep only essential ones

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Félix Malfait
2026-04-09 11:21:57 +02:00
co-authored by Claude Opus 4.6
parent c15ae95c5f
commit fa59e96762
15 changed files with 4 additions and 73 deletions
@@ -1,5 +1,2 @@
// Maximum total recipients (To + Cc + Bcc) allowed per outbound email.
// Mirrors `MAX_EMAIL_RECIPIENTS` in twenty-server (email-tool.constants.ts).
// Surfaced on the frontend so the composer can disable Send and warn the user
// before the request is rejected by the backend.
// Mirrors MAX_EMAIL_RECIPIENTS in twenty-server (email-tool.constants.ts).
export const MAX_EMAIL_RECIPIENTS = 100;
@@ -6,9 +6,6 @@ import { SettingsPath } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
// Wires the compose-email side panel to the record currently shown in the
// page (Person, Company or Opportunity). Used by both the inbox header `+`
// button and the empty-inbox CTA so they stay in sync.
export const useComposeEmailForTargetRecord = () => {
const targetRecord = useTargetRecord();
const { openComposeEmailInSidePanel } = useOpenComposeEmailInSidePanel();
@@ -12,9 +12,6 @@ type UseEmailComposerStateArgs = {
onSent?: () => void;
};
// Light-weight count of comma-separated recipients. Mirrors the backend
// `parseCommaSeparatedEmails` splitter so the disabled state matches what the
// server would actually accept — we just count, no validation.
const countRecipients = (csv: string): number =>
csv
.split(',')
@@ -5,16 +5,11 @@ import { useOpenComposeEmailInSidePanel } from '@/side-panel/hooks/useOpenCompos
import { isDefined } from 'twenty-shared/utils';
type UseOpenEmailInAppOrFallbackOptions = {
// When true the underlying connected-account query is skipped entirely,
// avoiding an unnecessary network request for non-email field types or when
// the click action is not OPEN_IN_APP.
skip?: boolean;
};
// Opens the in-app email composer for the given email address. When the user
// has no connected account we cannot send through the side panel, so we fall
// back to the OS-level mailto handler instead of redirecting to settings —
// the user explicitly opted into "Open in app" and the link is still useful.
// Falls back to mailto: when no connected account exists, rather than
// redirecting to settings.
export const useOpenEmailInAppOrFallback = (
options?: UseOpenEmailInAppOrFallbackOptions,
) => {
@@ -7,11 +7,6 @@ type UseResolveDefaultEmailRecipientParams = {
recordId: string | null | undefined;
};
// Resolves the default `to` email recipient for a record so that the email
// composer can be opened pre-filled. The recipient depends on the object type:
// - person: the person's primary email
// - company: the primary email of the first person attached to the company
// - opportunity: the primary email of the opportunity's point of contact
export const useResolveDefaultEmailRecipient = ({
objectNameSingular,
recordId,
@@ -12,10 +12,6 @@ import { formatFileSize } from '@/file/utils/formatFileSize';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { logError } from '~/utils/logError';
// Email attachment uploads return the same `FileWithSignedUrlDTO` shape as
// every other typed upload mutation; we declare it locally to avoid depending
// on a codegen-generated document for this mutation (see comment in
// uploadEmailAttachmentFile.ts).
type UploadEmailAttachmentFileResponse = {
uploadEmailAttachmentFile: {
id: string;
@@ -38,10 +34,6 @@ export const useUploadEmailAttachment = () => {
const { enqueueSuccessSnackBar, enqueueErrorSnackBar } = useSnackBar();
const { t } = useLingui();
// We reuse `WorkflowAttachment` as the in-memory shape because the SendEmail
// mutation input mirrors it field-for-field — sharing the type keeps the
// composer wiring trivial. The persisted file lives under the
// `email-attachment` folder, not the workflow folder.
const uploadEmailAttachment = async (
file: File,
): Promise<WorkflowAttachment | null> => {
@@ -2,10 +2,6 @@ import { isNonEmptyString } from '@sniptt/guards';
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
// Reads `record.emails.primaryEmail` defensively. `ObjectRecord` is typed as
// `Record<string, any>` so accessing nested fields is unchecked at compile
// time — we narrow at runtime so callers can rely on a `string | null` result
// without sprinkling `as` casts at every call site.
export const getPrimaryEmailFromRecord = (
record: ObjectRecord,
): string | null => {
@@ -26,17 +26,10 @@ export const ComposeEmailCommand = () => {
const objectNameSingular = objectMetadataItem?.nameSingular ?? null;
const isPerson = objectNameSingular === CoreObjectNameSingular.Person;
// Bulk means more than one record is selected *or* the user is in
// exclusion/"select all" mode where selectedRecords is empty but the
// graphqlFilter encodes the full target set.
const isBulkPerson =
isPerson &&
(selectedRecords.length > 1 || targetedRecordsRule.mode === 'exclusion');
// For bulk Person selections we fetch emails directly from the server so
// we always have the data regardless of which columns are visible in the
// current view. Capped at MAX_EMAIL_RECIPIENTS since the backend will
// reject anything above that anyway.
const { records: bulkPersonRecords, loading: bulkLoading } =
useFindManyRecords({
objectNameSingular: CoreObjectNameSingular.Person,
@@ -46,8 +39,6 @@ export const ComposeEmailCommand = () => {
skip: !isBulkPerson,
});
// For single-record selection we use the shared resolver which handles
// the per-object-type fetching (Person, Company, Opportunity).
const singleSelectedRecordId = !isBulkPerson
? (selectedRecords[0]?.id ?? null)
: null;
@@ -11,9 +11,6 @@ export const EmailsFieldDisplay = () => {
const { copyToClipboard } = useCopyToClipboard();
const { t } = useLingui();
// Email fields default to opening the in-app composer when no setting is
// explicitly stored. The mailto-based <a> stays as the rendered href so the
// link is still right-clickable / openable in a new tab.
const onClickAction =
fieldDefinition.metadata.settings?.clickAction ??
FieldMetadataSettingsOnClickAction.OPEN_IN_APP;
@@ -21,8 +18,6 @@ export const EmailsFieldDisplay = () => {
const isOpenInApp =
onClickAction === FieldMetadataSettingsOnClickAction.OPEN_IN_APP;
// Only fire the connected-account query when the click action is
// OPEN_IN_APP — COPY and OPEN_LINK don't need it.
const { openEmail } = useOpenEmailInAppOrFallback({ skip: !isOpenInApp });
const handleEmailClick = (
@@ -43,7 +38,6 @@ export const EmailsFieldDisplay = () => {
return;
}
// OPEN_LINK: let the native <a href="mailto:…"> behaviour handle it.
};
return <EmailsDisplay value={fieldValue} onEmailClick={handleEmailClick} />;
@@ -22,8 +22,6 @@ export const useGetSecondaryRecordTableCellButton = () => {
const isEmailField = isFieldEmails(fieldDefinition);
// Only fire the connected-account query for email fields — phone and link
// fields never use the in-app composer so the request would be wasted.
const { openEmail } = useOpenEmailInAppOrFallback({ skip: !isEmailField });
const fieldValue = useRecordFieldValue<
@@ -39,8 +37,6 @@ export const useGetSecondaryRecordTableCellButton = () => {
return [];
}
// Email fields default to opening the in-app composer; other field types
// default to opening the platform link handler.
const defaultClickAction = isEmailField
? FieldMetadataSettingsOnClickAction.OPEN_IN_APP
: FieldMetadataSettingsOnClickAction.OPEN_LINK;
@@ -48,9 +44,6 @@ export const useGetSecondaryRecordTableCellButton = () => {
const mainActionOnClick =
fieldDefinition.metadata.settings?.clickAction ?? defaultClickAction;
// The secondary button always exposes the "other" useful action: if main is
// a copy then offer an open action, otherwise offer copy. For emails the
// open action is the in-app composer rather than mailto.
const openActionForFieldType = isEmailField
? FieldMetadataSettingsOnClickAction.OPEN_IN_APP
: FieldMetadataSettingsOnClickAction.OPEN_LINK;
@@ -37,8 +37,6 @@ export const SettingsDataModelFieldOnClickActionForm = ({
const isEmailField = fieldType === FieldMetadataType.EMAILS;
// For email fields we offer (and default to) opening the in-app composer.
// Other field types keep "Open as link" as the default.
const defaultClickAction = isEmailField
? FieldMetadataSettingsOnClickAction.OPEN_IN_APP
: FieldMetadataSettingsOnClickAction.OPEN_LINK;
@@ -45,9 +45,6 @@ export class FileEmailAttachmentService {
},
);
// Email attachments are transient: once the email is sent, the local
// copy is no longer needed for retrieval. Mark as temporary so the
// file deletion job can sweep it.
const savedFile = await this.fileStorageService.writeFile({
sourceFile: sanitizedFile,
resourcePath: name,
@@ -1,3 +1 @@
// Maximum total recipients (To + Cc + Bcc) allowed per outbound email.
// Mirrored on the frontend in MAX_EMAIL_RECIPIENTS to provide early UX feedback.
export const MAX_EMAIL_RECIPIENTS = 100;
@@ -943,11 +943,6 @@ export const STANDARD_COMMAND_MENU_ITEMS = {
hotKeys: null,
payload: { path: '/settings/updates' },
},
// Per-object Send Email items: same engine component as the global
// Compose Email command, but scoped to a record selection so the composer
// can be opened pre-filled with the selected record's email address.
// Person allows >= 1 so it works both from a record show page and from a
// bulk selection on the people index page.
composeEmailToPerson: {
universalIdentifier: 'f01d4b8b-2b4e-4ae0-9c6f-0b9a9a3e5b21',
label: 'Send Email',
@@ -1,10 +1,6 @@
import { Field, InputType } from '@nestjs/graphql';
// Mirrors the `WorkflowAttachment` shape from twenty-shared (id/name/size/type/
// createdAt). Inlined as a GraphQL input so the side-panel composer can pass
// already-uploaded files without depending on workflow types. If you change
// either side, keep both in sync — `EmailComposerService.getAttachments`
// resolves files by id, so the field names matter.
// Must stay in sync with WorkflowAttachment from twenty-shared.
@InputType()
export class SendEmailAttachmentInput {
@Field(() => String)