diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql index 4d8d52de11..7db61ae21f 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql +++ b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql @@ -2789,6 +2789,7 @@ type DuplicatedDashboard { type SendEmailOutput { success: Boolean! error: String + messageThreadId: String } type Analytics { @@ -4609,6 +4610,7 @@ input SendEmailInput { subject: String! body: String! inReplyTo: String + draftMessageId: String files: [SendEmailAttachmentInput!] } diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.ts b/packages/twenty-client-sdk/src/metadata/generated/schema.ts index c75e835726..61fbf816a3 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/schema.ts +++ b/packages/twenty-client-sdk/src/metadata/generated/schema.ts @@ -2453,6 +2453,7 @@ export interface DuplicatedDashboard { export interface SendEmailOutput { success: Scalars['Boolean'] error?: Scalars['String'] + messageThreadId?: Scalars['String'] __typename: 'SendEmailOutput' } @@ -5611,6 +5612,7 @@ export interface DuplicatedDashboardGenqlSelection{ export interface SendEmailOutputGenqlSelection{ success?: boolean | number error?: boolean | number + messageThreadId?: boolean | number __typename?: boolean | number __scalar?: boolean | number } @@ -6590,7 +6592,7 @@ export interface EditSsoInput {id: Scalars['UUID'],status: SSOIdentityProviderSt export interface CreateCalendarEventInput {connectedAccountId: Scalars['String'],title: Scalars['String'],description?: (Scalars['String'] | null),location?: (Scalars['String'] | null),startsAt: Scalars['String'],endsAt: Scalars['String'],isFullDay?: (Scalars['Boolean'] | null),timeZone?: (Scalars['String'] | null),attendees?: (Scalars['String'] | null),sendInvitations?: (Scalars['Boolean'] | null),addConferencing?: (Scalars['Boolean'] | null)} -export interface SendEmailInput {connectedAccountId: Scalars['String'],to: Scalars['String'],cc?: (Scalars['String'] | null),bcc?: (Scalars['String'] | null),subject: Scalars['String'],body: Scalars['String'],inReplyTo?: (Scalars['String'] | null),files?: (SendEmailAttachmentInput[] | null)} +export interface SendEmailInput {connectedAccountId: Scalars['String'],to: Scalars['String'],cc?: (Scalars['String'] | null),bcc?: (Scalars['String'] | null),subject: Scalars['String'],body: Scalars['String'],inReplyTo?: (Scalars['String'] | null),draftMessageId?: (Scalars['String'] | null),files?: (SendEmailAttachmentInput[] | null)} export interface SendEmailAttachmentInput {id: Scalars['String'],name: Scalars['String']} diff --git a/packages/twenty-client-sdk/src/metadata/generated/types.ts b/packages/twenty-client-sdk/src/metadata/generated/types.ts index ac68e81386..a3d06304bc 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/types.ts +++ b/packages/twenty-client-sdk/src/metadata/generated/types.ts @@ -5549,6 +5549,9 @@ export default { "error": [ 1 ], + "messageThreadId": [ + 1 + ], "__typename": [ 1 ] @@ -11702,6 +11705,9 @@ export default { "inReplyTo": [ 1 ], + "draftMessageId": [ + 1 + ], "files": [ 491 ], diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 84b5ea4487..cbff9fd2fb 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -5004,6 +5004,7 @@ export type SendEmailInput = { body: Scalars['String']['input']; cc?: InputMaybe; connectedAccountId: Scalars['String']['input']; + draftMessageId?: InputMaybe; files?: InputMaybe>; inReplyTo?: InputMaybe; subject: Scalars['String']['input']; @@ -5013,6 +5014,7 @@ export type SendEmailInput = { export type SendEmailOutput = { __typename?: 'SendEmailOutput'; error?: Maybe; + messageThreadId?: Maybe; success: Scalars['Boolean']['output']; }; @@ -6379,7 +6381,7 @@ export type SendEmailMutationVariables = Exact<{ }>; -export type SendEmailMutation = { __typename?: 'Mutation', sendEmail: { __typename?: 'SendEmailOutput', success: boolean, error?: string | null } }; +export type SendEmailMutation = { __typename?: 'Mutation', sendEmail: { __typename?: 'SendEmailOutput', success: boolean, error?: string | null, messageThreadId?: string | null } }; export type SendMessageCampaignMutationVariables = Exact<{ input: SendMessageCampaignInput; @@ -8719,7 +8721,7 @@ export const ViewGroupFragmentFragmentDoc = {"kind":"Document","definitions":[{" export const ViewFragmentFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"View"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"objectMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"position"}},{"kind":"Field","name":{"kind":"Name","value":"isCompact"}},{"kind":"Field","name":{"kind":"Name","value":"openRecordIn"}},{"kind":"Field","name":{"kind":"Name","value":"kanbanAggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"kanbanAggregateOperationFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"mainGroupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"shouldHideEmptyGroups"}},{"kind":"Field","name":{"kind":"Name","value":"kanbanColumnWidth"}},{"kind":"Field","name":{"kind":"Name","value":"anyFieldFilterValue"}},{"kind":"Field","name":{"kind":"Name","value":"calendarFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"calendarLayout"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"createdByUserWorkspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"viewFields"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewFieldFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"viewFieldGroups"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewFieldGroupFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"viewFilters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewFilterFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"viewFilterGroups"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewFilterGroupFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"viewSorts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewSortFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"viewGroups"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewGroupFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewFieldFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ViewField"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"fieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}},{"kind":"Field","name":{"kind":"Name","value":"isVisible"}},{"kind":"Field","name":{"kind":"Name","value":"position"}},{"kind":"Field","name":{"kind":"Name","value":"size"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"viewFieldGroupId"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewFieldGroupFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ViewFieldGroup"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"position"}},{"kind":"Field","name":{"kind":"Name","value":"isVisible"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}},{"kind":"Field","name":{"kind":"Name","value":"viewFields"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewFieldFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewFilterFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ViewFilter"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"fieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"operand"}},{"kind":"Field","name":{"kind":"Name","value":"value"}},{"kind":"Field","name":{"kind":"Name","value":"viewFilterGroupId"}},{"kind":"Field","name":{"kind":"Name","value":"positionInViewFilterGroup"}},{"kind":"Field","name":{"kind":"Name","value":"subFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"relationTargetFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewFilterGroupFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ViewFilterGroup"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"parentViewFilterGroupId"}},{"kind":"Field","name":{"kind":"Name","value":"logicalOperator"}},{"kind":"Field","name":{"kind":"Name","value":"positionInViewFilterGroup"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewSortFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ViewSort"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"fieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"direction"}},{"kind":"Field","name":{"kind":"Name","value":"subFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewGroupFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ViewGroup"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"isVisible"}},{"kind":"Field","name":{"kind":"Name","value":"fieldValue"}},{"kind":"Field","name":{"kind":"Name","value":"position"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}}]}}]} as unknown as DocumentNode; export const PreviewMessageCampaignAudienceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"PreviewMessageCampaignAudience"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PreviewMessageCampaignAudienceInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"previewMessageCampaignAudience"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalMembers"}},{"kind":"Field","name":{"kind":"Name","value":"withoutEmail"}},{"kind":"Field","name":{"kind":"Name","value":"duplicateEmails"}},{"kind":"Field","name":{"kind":"Name","value":"globallyUnsubscribed"}},{"kind":"Field","name":{"kind":"Name","value":"topicUnsubscribed"}},{"kind":"Field","name":{"kind":"Name","value":"sendable"}}]}}]}}]} as unknown as DocumentNode; export const UnsubscribeTopicsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UnsubscribeTopics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"unsubscribeTopics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}}]}}]}}]} as unknown as DocumentNode; -export const SendEmailDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SendEmail"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SendEmailInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"sendEmail"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"success"}},{"kind":"Field","name":{"kind":"Name","value":"error"}}]}}]}}]} as unknown as DocumentNode; +export const SendEmailDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SendEmail"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SendEmailInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"sendEmail"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"success"}},{"kind":"Field","name":{"kind":"Name","value":"error"}},{"kind":"Field","name":{"kind":"Name","value":"messageThreadId"}}]}}]}}]} as unknown as DocumentNode; export const SendMessageCampaignDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SendMessageCampaign"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SendMessageCampaignInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"sendMessageCampaign"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"campaignId"}},{"kind":"Field","name":{"kind":"Name","value":"queuedCount"}},{"kind":"Field","name":{"kind":"Name","value":"skipped"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"noEmail"}},{"kind":"Field","name":{"kind":"Name","value":"deduped"}},{"kind":"Field","name":{"kind":"Name","value":"overCap"}}]}}]}}]}}]} as unknown as DocumentNode; export const ActivateSkillDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ActivateSkill"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activateSkill"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SkillFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SkillFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Skill"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"content"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; export const ArchiveChatThreadDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ArchiveChatThread"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archiveChatThread"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}}]} as unknown as DocumentNode; diff --git a/packages/twenty-front/src/generated/graphql.ts b/packages/twenty-front/src/generated/graphql.ts index fd5e4e1d51..7d72f99720 100644 --- a/packages/twenty-front/src/generated/graphql.ts +++ b/packages/twenty-front/src/generated/graphql.ts @@ -545,6 +545,7 @@ export type TimelineThread = { firstParticipant: TimelineThreadParticipant; id: Scalars['UUID']['output']; lastMessageBody: Scalars['String']['output']; + lastMessageIsDraft: Scalars['Boolean']['output']; lastMessageReceivedAt: Scalars['DateTime']['output']; lastTwoParticipants: Array; numberOfMessagesInThread: Scalars['Float']['output']; @@ -708,9 +709,9 @@ export type GetTimelineCalendarEventsFromObjectRecordQuery = { __typename?: 'Que export type ParticipantFragmentFragment = { __typename?: 'TimelineThreadParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }; -export type TimelineThreadFragmentFragment = { __typename?: 'TimelineThread', id: any, read: boolean, visibility: MessageChannelVisibility, lastMessageReceivedAt: string, lastMessageBody: string, subject: string, numberOfMessagesInThread: number, participantCount: number, firstParticipant: { __typename?: 'TimelineThreadParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }, lastTwoParticipants: Array<{ __typename?: 'TimelineThreadParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }> }; +export type TimelineThreadFragmentFragment = { __typename?: 'TimelineThread', id: any, read: boolean, visibility: MessageChannelVisibility, lastMessageReceivedAt: string, lastMessageBody: string, subject: string, numberOfMessagesInThread: number, participantCount: number, lastMessageIsDraft: boolean, firstParticipant: { __typename?: 'TimelineThreadParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }, lastTwoParticipants: Array<{ __typename?: 'TimelineThreadParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }> }; -export type TimelineThreadsWithTotalFragmentFragment = { __typename?: 'TimelineThreadsWithTotal', totalNumberOfThreads: number, relatedPersonIds: Array, timelineThreads: Array<{ __typename?: 'TimelineThread', id: any, read: boolean, visibility: MessageChannelVisibility, lastMessageReceivedAt: string, lastMessageBody: string, subject: string, numberOfMessagesInThread: number, participantCount: number, firstParticipant: { __typename?: 'TimelineThreadParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }, lastTwoParticipants: Array<{ __typename?: 'TimelineThreadParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }> }> }; +export type TimelineThreadsWithTotalFragmentFragment = { __typename?: 'TimelineThreadsWithTotal', totalNumberOfThreads: number, relatedPersonIds: Array, timelineThreads: Array<{ __typename?: 'TimelineThread', id: any, read: boolean, visibility: MessageChannelVisibility, lastMessageReceivedAt: string, lastMessageBody: string, subject: string, numberOfMessagesInThread: number, participantCount: number, lastMessageIsDraft: boolean, firstParticipant: { __typename?: 'TimelineThreadParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }, lastTwoParticipants: Array<{ __typename?: 'TimelineThreadParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }> }> }; export type GetTimelineThreadsFromObjectRecordQueryVariables = Exact<{ objectNameSingular: Scalars['String']['input']; @@ -720,7 +721,7 @@ export type GetTimelineThreadsFromObjectRecordQueryVariables = Exact<{ }>; -export type GetTimelineThreadsFromObjectRecordQuery = { __typename?: 'Query', getTimelineThreadsFromObjectRecord: { __typename?: 'TimelineThreadsWithTotal', totalNumberOfThreads: number, relatedPersonIds: Array, timelineThreads: Array<{ __typename?: 'TimelineThread', id: any, read: boolean, visibility: MessageChannelVisibility, lastMessageReceivedAt: string, lastMessageBody: string, subject: string, numberOfMessagesInThread: number, participantCount: number, firstParticipant: { __typename?: 'TimelineThreadParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }, lastTwoParticipants: Array<{ __typename?: 'TimelineThreadParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }> }> } }; +export type GetTimelineThreadsFromObjectRecordQuery = { __typename?: 'Query', getTimelineThreadsFromObjectRecord: { __typename?: 'TimelineThreadsWithTotal', totalNumberOfThreads: number, relatedPersonIds: Array, timelineThreads: Array<{ __typename?: 'TimelineThread', id: any, read: boolean, visibility: MessageChannelVisibility, lastMessageReceivedAt: string, lastMessageBody: string, subject: string, numberOfMessagesInThread: number, participantCount: number, lastMessageIsDraft: boolean, firstParticipant: { __typename?: 'TimelineThreadParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }, lastTwoParticipants: Array<{ __typename?: 'TimelineThreadParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }> }> } }; export type SearchQueryVariables = Exact<{ searchInput: Scalars['String']['input']; @@ -873,11 +874,11 @@ export const TimelineCalendarEventParticipantFragmentFragmentDoc = {"kind":"Docu export const TimelineCalendarEventFragmentFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TimelineCalendarEventFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TimelineCalendarEvent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"startsAt"}},{"kind":"Field","name":{"kind":"Name","value":"endsAt"}},{"kind":"Field","name":{"kind":"Name","value":"isFullDay"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"participants"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TimelineCalendarEventParticipantFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TimelineCalendarEventParticipantFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TimelineCalendarEventParticipant"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"personId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceMemberId"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}},{"kind":"Field","name":{"kind":"Name","value":"handle"}}]}}]} as unknown as DocumentNode; export const TimelineCalendarEventsWithTotalFragmentFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TimelineCalendarEventsWithTotalFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TimelineCalendarEventsWithTotal"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalNumberOfCalendarEvents"}},{"kind":"Field","name":{"kind":"Name","value":"relatedPersonIds"}},{"kind":"Field","name":{"kind":"Name","value":"timelineCalendarEvents"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TimelineCalendarEventFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TimelineCalendarEventParticipantFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TimelineCalendarEventParticipant"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"personId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceMemberId"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}},{"kind":"Field","name":{"kind":"Name","value":"handle"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TimelineCalendarEventFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TimelineCalendarEvent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"startsAt"}},{"kind":"Field","name":{"kind":"Name","value":"endsAt"}},{"kind":"Field","name":{"kind":"Name","value":"isFullDay"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"participants"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TimelineCalendarEventParticipantFragment"}}]}}]}}]} as unknown as DocumentNode; export const ParticipantFragmentFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ParticipantFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TimelineThreadParticipant"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"personId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceMemberId"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}},{"kind":"Field","name":{"kind":"Name","value":"handle"}}]}}]} as unknown as DocumentNode; -export const TimelineThreadFragmentFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TimelineThreadFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TimelineThread"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"read"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"firstParticipant"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ParticipantFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"lastTwoParticipants"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ParticipantFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"lastMessageReceivedAt"}},{"kind":"Field","name":{"kind":"Name","value":"lastMessageBody"}},{"kind":"Field","name":{"kind":"Name","value":"subject"}},{"kind":"Field","name":{"kind":"Name","value":"numberOfMessagesInThread"}},{"kind":"Field","name":{"kind":"Name","value":"participantCount"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ParticipantFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TimelineThreadParticipant"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"personId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceMemberId"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}},{"kind":"Field","name":{"kind":"Name","value":"handle"}}]}}]} as unknown as DocumentNode; -export const TimelineThreadsWithTotalFragmentFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TimelineThreadsWithTotalFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TimelineThreadsWithTotal"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalNumberOfThreads"}},{"kind":"Field","name":{"kind":"Name","value":"relatedPersonIds"}},{"kind":"Field","name":{"kind":"Name","value":"timelineThreads"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TimelineThreadFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ParticipantFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TimelineThreadParticipant"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"personId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceMemberId"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}},{"kind":"Field","name":{"kind":"Name","value":"handle"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TimelineThreadFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TimelineThread"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"read"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"firstParticipant"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ParticipantFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"lastTwoParticipants"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ParticipantFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"lastMessageReceivedAt"}},{"kind":"Field","name":{"kind":"Name","value":"lastMessageBody"}},{"kind":"Field","name":{"kind":"Name","value":"subject"}},{"kind":"Field","name":{"kind":"Name","value":"numberOfMessagesInThread"}},{"kind":"Field","name":{"kind":"Name","value":"participantCount"}}]}}]} as unknown as DocumentNode; +export const TimelineThreadFragmentFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TimelineThreadFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TimelineThread"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"read"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"firstParticipant"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ParticipantFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"lastTwoParticipants"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ParticipantFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"lastMessageReceivedAt"}},{"kind":"Field","name":{"kind":"Name","value":"lastMessageBody"}},{"kind":"Field","name":{"kind":"Name","value":"subject"}},{"kind":"Field","name":{"kind":"Name","value":"numberOfMessagesInThread"}},{"kind":"Field","name":{"kind":"Name","value":"participantCount"}},{"kind":"Field","name":{"kind":"Name","value":"lastMessageIsDraft"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ParticipantFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TimelineThreadParticipant"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"personId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceMemberId"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}},{"kind":"Field","name":{"kind":"Name","value":"handle"}}]}}]} as unknown as DocumentNode; +export const TimelineThreadsWithTotalFragmentFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TimelineThreadsWithTotalFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TimelineThreadsWithTotal"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalNumberOfThreads"}},{"kind":"Field","name":{"kind":"Name","value":"relatedPersonIds"}},{"kind":"Field","name":{"kind":"Name","value":"timelineThreads"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TimelineThreadFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ParticipantFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TimelineThreadParticipant"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"personId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceMemberId"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}},{"kind":"Field","name":{"kind":"Name","value":"handle"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TimelineThreadFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TimelineThread"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"read"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"firstParticipant"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ParticipantFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"lastTwoParticipants"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ParticipantFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"lastMessageReceivedAt"}},{"kind":"Field","name":{"kind":"Name","value":"lastMessageBody"}},{"kind":"Field","name":{"kind":"Name","value":"subject"}},{"kind":"Field","name":{"kind":"Name","value":"numberOfMessagesInThread"}},{"kind":"Field","name":{"kind":"Name","value":"participantCount"}},{"kind":"Field","name":{"kind":"Name","value":"lastMessageIsDraft"}}]}}]} as unknown as DocumentNode; export const WorkflowDiffFragmentFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkflowDiffFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkflowVersionStepChanges"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"triggerDiff"}},{"kind":"Field","name":{"kind":"Name","value":"stepsDiff"}}]}}]} as unknown as DocumentNode; export const GetTimelineCalendarEventsFromObjectRecordDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetTimelineCalendarEventsFromObjectRecord"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"objectNameSingular"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"recordId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"page"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"pageSize"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getTimelineCalendarEventsFromObjectRecord"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"objectNameSingular"},"value":{"kind":"Variable","name":{"kind":"Name","value":"objectNameSingular"}}},{"kind":"Argument","name":{"kind":"Name","value":"recordId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"recordId"}}},{"kind":"Argument","name":{"kind":"Name","value":"page"},"value":{"kind":"Variable","name":{"kind":"Name","value":"page"}}},{"kind":"Argument","name":{"kind":"Name","value":"pageSize"},"value":{"kind":"Variable","name":{"kind":"Name","value":"pageSize"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TimelineCalendarEventsWithTotalFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TimelineCalendarEventParticipantFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TimelineCalendarEventParticipant"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"personId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceMemberId"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}},{"kind":"Field","name":{"kind":"Name","value":"handle"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TimelineCalendarEventFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TimelineCalendarEvent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"startsAt"}},{"kind":"Field","name":{"kind":"Name","value":"endsAt"}},{"kind":"Field","name":{"kind":"Name","value":"isFullDay"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"participants"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TimelineCalendarEventParticipantFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TimelineCalendarEventsWithTotalFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TimelineCalendarEventsWithTotal"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalNumberOfCalendarEvents"}},{"kind":"Field","name":{"kind":"Name","value":"relatedPersonIds"}},{"kind":"Field","name":{"kind":"Name","value":"timelineCalendarEvents"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TimelineCalendarEventFragment"}}]}}]}}]} as unknown as DocumentNode; -export const GetTimelineThreadsFromObjectRecordDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetTimelineThreadsFromObjectRecord"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"objectNameSingular"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"recordId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"page"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"pageSize"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getTimelineThreadsFromObjectRecord"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"objectNameSingular"},"value":{"kind":"Variable","name":{"kind":"Name","value":"objectNameSingular"}}},{"kind":"Argument","name":{"kind":"Name","value":"recordId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"recordId"}}},{"kind":"Argument","name":{"kind":"Name","value":"page"},"value":{"kind":"Variable","name":{"kind":"Name","value":"page"}}},{"kind":"Argument","name":{"kind":"Name","value":"pageSize"},"value":{"kind":"Variable","name":{"kind":"Name","value":"pageSize"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TimelineThreadsWithTotalFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ParticipantFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TimelineThreadParticipant"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"personId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceMemberId"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}},{"kind":"Field","name":{"kind":"Name","value":"handle"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TimelineThreadFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TimelineThread"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"read"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"firstParticipant"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ParticipantFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"lastTwoParticipants"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ParticipantFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"lastMessageReceivedAt"}},{"kind":"Field","name":{"kind":"Name","value":"lastMessageBody"}},{"kind":"Field","name":{"kind":"Name","value":"subject"}},{"kind":"Field","name":{"kind":"Name","value":"numberOfMessagesInThread"}},{"kind":"Field","name":{"kind":"Name","value":"participantCount"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TimelineThreadsWithTotalFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TimelineThreadsWithTotal"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalNumberOfThreads"}},{"kind":"Field","name":{"kind":"Name","value":"relatedPersonIds"}},{"kind":"Field","name":{"kind":"Name","value":"timelineThreads"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TimelineThreadFragment"}}]}}]}}]} as unknown as DocumentNode; +export const GetTimelineThreadsFromObjectRecordDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetTimelineThreadsFromObjectRecord"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"objectNameSingular"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"recordId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"page"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"pageSize"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getTimelineThreadsFromObjectRecord"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"objectNameSingular"},"value":{"kind":"Variable","name":{"kind":"Name","value":"objectNameSingular"}}},{"kind":"Argument","name":{"kind":"Name","value":"recordId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"recordId"}}},{"kind":"Argument","name":{"kind":"Name","value":"page"},"value":{"kind":"Variable","name":{"kind":"Name","value":"page"}}},{"kind":"Argument","name":{"kind":"Name","value":"pageSize"},"value":{"kind":"Variable","name":{"kind":"Name","value":"pageSize"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TimelineThreadsWithTotalFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ParticipantFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TimelineThreadParticipant"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"personId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceMemberId"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}},{"kind":"Field","name":{"kind":"Name","value":"handle"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TimelineThreadFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TimelineThread"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"read"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"firstParticipant"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ParticipantFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"lastTwoParticipants"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ParticipantFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"lastMessageReceivedAt"}},{"kind":"Field","name":{"kind":"Name","value":"lastMessageBody"}},{"kind":"Field","name":{"kind":"Name","value":"subject"}},{"kind":"Field","name":{"kind":"Name","value":"numberOfMessagesInThread"}},{"kind":"Field","name":{"kind":"Name","value":"participantCount"}},{"kind":"Field","name":{"kind":"Name","value":"lastMessageIsDraft"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TimelineThreadsWithTotalFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TimelineThreadsWithTotal"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalNumberOfThreads"}},{"kind":"Field","name":{"kind":"Name","value":"relatedPersonIds"}},{"kind":"Field","name":{"kind":"Name","value":"timelineThreads"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TimelineThreadFragment"}}]}}]}}]} as unknown as DocumentNode; export const SearchDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Search"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"searchInput"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"limit"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"after"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"excludedObjectNameSingulars"}},"type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"includedObjectNameSingulars"}},"type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ObjectRecordFilterInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"search"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"searchInput"},"value":{"kind":"Variable","name":{"kind":"Name","value":"searchInput"}}},{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}},{"kind":"Argument","name":{"kind":"Name","value":"after"},"value":{"kind":"Variable","name":{"kind":"Name","value":"after"}}},{"kind":"Argument","name":{"kind":"Name","value":"excludedObjectNameSingulars"},"value":{"kind":"Variable","name":{"kind":"Name","value":"excludedObjectNameSingulars"}}},{"kind":"Argument","name":{"kind":"Name","value":"includedObjectNameSingulars"},"value":{"kind":"Variable","name":{"kind":"Name","value":"includedObjectNameSingulars"}}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"recordId"}},{"kind":"Field","name":{"kind":"Name","value":"objectNameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"objectLabelSingular"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"imageUrl"}},{"kind":"Field","name":{"kind":"Name","value":"tsRankCD"}},{"kind":"Field","name":{"kind":"Name","value":"tsRank"}}]}},{"kind":"Field","name":{"kind":"Name","value":"cursor"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"endCursor"}}]}}]}}]}}]} as unknown as DocumentNode; export const ActivateWorkflowVersionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ActivateWorkflowVersion"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workflowVersionId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activateWorkflowVersion"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workflowVersionId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workflowVersionId"}}}]}]}}]} as unknown as DocumentNode; export const ComputeStepOutputSchemaDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ComputeStepOutputSchema"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ComputeStepOutputSchemaInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"computeStepOutputSchema"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode; diff --git a/packages/twenty-front/src/modules/activities/emails/components/EmailComposerFields.tsx b/packages/twenty-front/src/modules/activities/emails/components/EmailComposerFields.tsx index 8ae0cbb18c..52c91f1b82 100644 --- a/packages/twenty-front/src/modules/activities/emails/components/EmailComposerFields.tsx +++ b/packages/twenty-front/src/modules/activities/emails/components/EmailComposerFields.tsx @@ -73,7 +73,7 @@ export const EmailComposerFields = ({ @@ -87,13 +87,13 @@ export const EmailComposerFields = ({ <> @@ -101,12 +101,12 @@ export const EmailComposerFields = ({ )} ` - border-bottom: ${({ hideBottomBorder }) => - hideBottomBorder - ? 'none' - : `1px solid ${themeCssVariables.border.color.light}`}; - display: flex; - flex-direction: column; - padding: ${themeCssVariables.spacing[4]} ${themeCssVariables.spacing[0]}; -`; - -const StyledThreadMessageHeader = styled.div` - cursor: pointer; - display: flex; - flex-direction: column; - justify-content: space-between; - margin-bottom: ${themeCssVariables.spacing[2]}; - padding: ${themeCssVariables.spacing[0]} ${themeCssVariables.spacing[2]}; -`; - -const StyledThreadMessageBody = styled.div` - padding: ${themeCssVariables.spacing[0]} ${themeCssVariables.spacing[2]}; -`; - type EmailThreadMessageProps = { - body: string; - sentAt: string; - sender: EmailThreadMessageParticipant; - participants: EmailThreadMessageParticipant[]; + message: EmailThreadMessageWithSender; isExpanded?: boolean; hideBottomBorder?: boolean; + onDraftClick: (message: EmailThreadMessageWithSender) => void; }; export const EmailThreadMessage = ({ - body, - sentAt, - sender, - participants, + message, isExpanded = false, hideBottomBorder = false, + onDraftClick, }: EmailThreadMessageProps) => { const [isOpen, setIsOpen] = useState(isExpanded); - const receivers = participants.filter( + const receivers = message.messageParticipants.filter( (participant) => participant.role !== MessageParticipantRole.FROM, ); - if (!isDefined(sender) || receivers.length === 0) { + if ( + !isDefined(message.sender) || + (!message.isDraft && receivers.length === 0) + ) { return null; } + const { isDraft } = message; + const isRestricted = - body === FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED; + message.text === FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED; + + const handleRowClick = () => { + if (isRestricted) { + return; + } + + if (isDraft) { + onDraftClick(message); + + return; + } + + if (!isOpen) { + setIsOpen(true); + } + }; + + const handleHeaderClick = () => { + if (!isDraft && isOpen) { + setIsOpen(false); + } + }; return ( - !isOpen && setIsOpen(true)} - style={{ cursor: isOpen || isRestricted ? 'auto' : 'pointer' }} - > - isOpen && setIsOpen(false)}> - - {isOpen && } - - - {isRestricted ? ( - + - ) : isOpen ? ( - - ) : ( - - )} - - + {!isDraft && isOpen && ( + + )} + + } + > + {isRestricted ? ( + + ) : isDraft || !isOpen ? ( + + ) : ( + + )} + ); }; diff --git a/packages/twenty-front/src/modules/activities/emails/components/EmailThreadMessageLayout.tsx b/packages/twenty-front/src/modules/activities/emails/components/EmailThreadMessageLayout.tsx new file mode 100644 index 0000000000..e9a70aa630 --- /dev/null +++ b/packages/twenty-front/src/modules/activities/emails/components/EmailThreadMessageLayout.tsx @@ -0,0 +1,57 @@ +import { styled } from '@linaria/react'; +import { type ReactNode } from 'react'; +import { themeCssVariables } from 'twenty-ui/theme-constants'; + +const StyledThreadMessage = styled.div<{ hideBottomBorder?: boolean }>` + border-bottom: ${({ hideBottomBorder }) => + hideBottomBorder + ? 'none' + : `1px solid ${themeCssVariables.border.color.light}`}; + display: flex; + flex-direction: column; + padding: ${themeCssVariables.spacing[4]} ${themeCssVariables.spacing[0]}; +`; + +const StyledHeader = styled.div<{ isClickable?: boolean }>` + cursor: ${({ isClickable }) => (isClickable ? 'pointer' : 'auto')}; + display: flex; + flex-direction: column; + justify-content: space-between; + margin-bottom: ${themeCssVariables.spacing[2]}; + padding: ${themeCssVariables.spacing[0]} ${themeCssVariables.spacing[2]}; +`; + +const StyledBody = styled.div` + padding: ${themeCssVariables.spacing[0]} ${themeCssVariables.spacing[2]}; +`; + +type EmailThreadMessageLayoutProps = { + header: ReactNode; + children: ReactNode; + hideBottomBorder?: boolean; + isRowClickable?: boolean; + isHeaderClickable?: boolean; + onRowClick?: () => void; + onHeaderClick?: () => void; +}; + +export const EmailThreadMessageLayout = ({ + header, + children, + hideBottomBorder = false, + isRowClickable = false, + isHeaderClickable = false, + onRowClick, + onHeaderClick, +}: EmailThreadMessageLayoutProps) => ( + + + {header} + + {children} + +); diff --git a/packages/twenty-front/src/modules/activities/emails/components/EmailThreadPreview.tsx b/packages/twenty-front/src/modules/activities/emails/components/EmailThreadPreview.tsx index 42efe7d4e0..3bd7cadf9e 100644 --- a/packages/twenty-front/src/modules/activities/emails/components/EmailThreadPreview.tsx +++ b/packages/twenty-front/src/modules/activities/emails/components/EmailThreadPreview.tsx @@ -5,9 +5,10 @@ import { EmailThreadNotShared } from '@/activities/emails/components/EmailThread import { useOpenRecordInSidePanel } from '@/side-panel/hooks/useOpenRecordInSidePanel'; import { useContext } from 'react'; +import { t } from '@lingui/core/macro'; import { CoreObjectNameSingular } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; -import { Avatar } from 'twenty-ui/data-display'; +import { Avatar, Tag } from 'twenty-ui/data-display'; import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants'; import { MessageChannelVisibility, @@ -173,6 +174,9 @@ export const EmailThreadPreview = ({ thread }: EmailThreadPreviewProps) => { )} {visibility === MessageChannelVisibility.SHARE_EVERYTHING && ( <> + {thread.lastMessageIsDraft && ( + + )} {thread.subject} {thread.lastMessageBody} diff --git a/packages/twenty-front/src/modules/activities/emails/graphql/mutations/sendEmail.ts b/packages/twenty-front/src/modules/activities/emails/graphql/mutations/sendEmail.ts index 14a0f15178..e7acb3f7e4 100644 --- a/packages/twenty-front/src/modules/activities/emails/graphql/mutations/sendEmail.ts +++ b/packages/twenty-front/src/modules/activities/emails/graphql/mutations/sendEmail.ts @@ -5,6 +5,7 @@ export const SEND_EMAIL = gql` sendEmail(input: $input) { success error + messageThreadId } } `; diff --git a/packages/twenty-front/src/modules/activities/emails/graphql/operation-signatures/factories/fetchAllThreadMessagesOperationSignatureFactory.ts b/packages/twenty-front/src/modules/activities/emails/graphql/operation-signatures/factories/fetchAllThreadMessagesOperationSignatureFactory.ts index b023208c15..f4f839ebed 100644 --- a/packages/twenty-front/src/modules/activities/emails/graphql/operation-signatures/factories/fetchAllThreadMessagesOperationSignatureFactory.ts +++ b/packages/twenty-front/src/modules/activities/emails/graphql/operation-signatures/factories/fetchAllThreadMessagesOperationSignatureFactory.ts @@ -31,6 +31,7 @@ export const fetchAllThreadMessagesOperationSignatureFactory: RecordGqlOperation subject: true, text: true, receivedAt: true, + isDraft: true, messageThread: { id: true, }, diff --git a/packages/twenty-front/src/modules/activities/emails/graphql/queries/fragments/timelineThreadFragment.ts b/packages/twenty-front/src/modules/activities/emails/graphql/queries/fragments/timelineThreadFragment.ts index 7d8f8ab9c3..a6d627d01d 100644 --- a/packages/twenty-front/src/modules/activities/emails/graphql/queries/fragments/timelineThreadFragment.ts +++ b/packages/twenty-front/src/modules/activities/emails/graphql/queries/fragments/timelineThreadFragment.ts @@ -18,6 +18,7 @@ export const timelineThreadFragment = gql` subject numberOfMessagesInThread participantCount + lastMessageIsDraft } ${participantFragment} `; diff --git a/packages/twenty-front/src/modules/activities/emails/hooks/useEmailComposerState.ts b/packages/twenty-front/src/modules/activities/emails/hooks/useEmailComposerState.ts index 0df7aed464..cebac50e67 100644 --- a/packages/twenty-front/src/modules/activities/emails/hooks/useEmailComposerState.ts +++ b/packages/twenty-front/src/modules/activities/emails/hooks/useEmailComposerState.ts @@ -3,13 +3,15 @@ import { MAX_EMAIL_RECIPIENTS } from 'twenty-shared/constants'; import { type EmailAttachment } from 'twenty-shared/types'; import { useSendEmail } from '@/activities/emails/hooks/useSendEmail'; +import { type EmailDraftPrefill } from '@/activities/emails/types/EmailDraftPrefill'; type UseEmailComposerStateArgs = { connectedAccountId: string; + draftPrefill?: EmailDraftPrefill | null; defaultTo?: string; defaultSubject?: string; defaultInReplyTo?: string; - onSent?: () => void; + onSent?: (messageThreadId: string | null) => void; }; const countRecipients = (csv: string): number => @@ -20,20 +22,29 @@ const countRecipients = (csv: string): number => export const useEmailComposerState = ({ connectedAccountId: initialConnectedAccountId, + draftPrefill, defaultTo = '', defaultSubject = '', defaultInReplyTo, onSent, }: UseEmailComposerStateArgs) => { + const initialTo = draftPrefill?.to ?? defaultTo; + const initialCc = draftPrefill?.cc ?? ''; + const initialBcc = draftPrefill?.bcc ?? ''; + const initialSubject = draftPrefill?.subject ?? defaultSubject; + const initialBody = draftPrefill?.body ?? ''; + const [connectedAccountId, setConnectedAccountId] = useState( initialConnectedAccountId, ); - const [to, setTo] = useState(defaultTo); - const [cc, setCc] = useState(''); - const [bcc, setBcc] = useState(''); - const [subject, setSubject] = useState(defaultSubject); - const [body, setBody] = useState(''); - const [showCcBcc, setShowCcBcc] = useState(false); + const [to, setTo] = useState(initialTo); + const [cc, setCc] = useState(initialCc); + const [bcc, setBcc] = useState(initialBcc); + const [subject, setSubject] = useState(initialSubject); + const [body, setBody] = useState(initialBody); + const [showCcBcc, setShowCcBcc] = useState( + initialCc.length > 0 || initialBcc.length > 0, + ); const [files, setFiles] = useState([]); const { sendEmail, loading } = useSendEmail(); @@ -60,7 +71,7 @@ export const useEmailComposerState = ({ const trimmedCc = cc.trim(); const trimmedBcc = bcc.trim(); - const success = await sendEmail({ + const { success, messageThreadId } = await sendEmail({ connectedAccountId, to: trimmedTo, cc: trimmedCc || undefined, @@ -68,11 +79,12 @@ export const useEmailComposerState = ({ subject, body, inReplyTo: defaultInReplyTo, + draftMessageId: draftPrefill?.messageId, files: files.length > 0 ? files : undefined, }); if (success) { - onSent?.(); + onSent?.(messageThreadId); } }, [ connectedAccountId, @@ -82,6 +94,7 @@ export const useEmailComposerState = ({ subject, body, defaultInReplyTo, + draftPrefill?.messageId, files, sendEmail, onSent, @@ -108,8 +121,11 @@ export const useEmailComposerState = ({ handleSend, loading, canSend, - defaultTo, - defaultSubject, + initialTo, + initialCc, + initialBcc, + initialSubject, + initialBody, recipientCount, exceedsRecipientLimit, maxRecipients: MAX_EMAIL_RECIPIENTS, diff --git a/packages/twenty-front/src/modules/activities/emails/hooks/useReplyContext.ts b/packages/twenty-front/src/modules/activities/emails/hooks/useReplyContext.ts index 6a8f8acb67..a54535b6aa 100644 --- a/packages/twenty-front/src/modules/activities/emails/hooks/useReplyContext.ts +++ b/packages/twenty-front/src/modules/activities/emails/hooks/useReplyContext.ts @@ -32,15 +32,27 @@ export const useReplyContext = ( return null; } - const lastMessage = messages[messages.length - 1]; + const sentMessages = messages.filter((message) => !message.isDraft); + const lastSentMessage = sentMessages[sentMessages.length - 1]; - if (!isDefined(lastMessage)) { - return null; + if (!isDefined(lastSentMessage)) { + if (messages.length === 0) { + return null; + } + + return { + loading: false, + to: '', + subject: '', + inReplyTo: '', + connectedAccountId, + connectedAccountProvider, + }; } - const senderHandle = lastMessage.sender?.handle ?? ''; + const senderHandle = lastSentMessage.sender?.handle ?? ''; - const rawSubject = lastMessage.subject ?? ''; + const rawSubject = lastSentMessage.subject ?? ''; const subject = rawSubject.startsWith('Re: ') ? rawSubject : `Re: ${rawSubject}`; @@ -49,7 +61,7 @@ export const useReplyContext = ( loading: false, to: senderHandle, subject, - inReplyTo: lastMessage.headerMessageId ?? '', + inReplyTo: lastSentMessage.headerMessageId ?? '', connectedAccountId, connectedAccountProvider, }; diff --git a/packages/twenty-front/src/modules/activities/emails/hooks/useSendEmail.ts b/packages/twenty-front/src/modules/activities/emails/hooks/useSendEmail.ts index e7d8f66435..fb272d52fb 100644 --- a/packages/twenty-front/src/modules/activities/emails/hooks/useSendEmail.ts +++ b/packages/twenty-front/src/modules/activities/emails/hooks/useSendEmail.ts @@ -12,6 +12,11 @@ import { type SendEmailMutationVariables, } from '~/generated-metadata/graphql'; +type SendEmailResult = { + success: boolean; + messageThreadId: string | null; +}; + type SendEmailParams = { connectedAccountId: string; to: string; @@ -20,6 +25,7 @@ type SendEmailParams = { subject: string; body: string; inReplyTo?: string; + draftMessageId?: string; files?: EmailAttachment[]; }; @@ -34,7 +40,7 @@ export const useSendEmail = () => { const { enqueueSuccessSnackBar, enqueueErrorSnackBar } = useSnackBar(); const sendEmail = useCallback( - async (params: SendEmailParams): Promise => { + async (params: SendEmailParams): Promise => { try { const result = await sendEmailMutation({ variables: { @@ -46,6 +52,7 @@ export const useSendEmail = () => { subject: params.subject, body: params.body, inReplyTo: params.inReplyTo, + draftMessageId: params.draftMessageId, files: params.files, }, }, @@ -65,20 +72,23 @@ export const useSendEmail = () => { ], }); - return true; + return { + success: true, + messageThreadId: result.data.sendEmail.messageThreadId ?? null, + }; } enqueueErrorSnackBar({ message: result.data?.sendEmail.error ?? t`Failed to send email`, }); - return false; + return { success: false, messageThreadId: null }; } catch { enqueueErrorSnackBar({ message: t`Failed to send email`, }); - return false; + return { success: false, messageThreadId: null }; } }, [ diff --git a/packages/twenty-front/src/modules/activities/emails/types/EmailDraftPrefill.ts b/packages/twenty-front/src/modules/activities/emails/types/EmailDraftPrefill.ts new file mode 100644 index 0000000000..dd1ad2d6c2 --- /dev/null +++ b/packages/twenty-front/src/modules/activities/emails/types/EmailDraftPrefill.ts @@ -0,0 +1,7 @@ +import { type EmailRecipients } from 'twenty-shared/workflow'; + +export type EmailDraftPrefill = EmailRecipients & { + messageId: string; + subject: string; + body: string; +}; diff --git a/packages/twenty-front/src/modules/activities/emails/types/EmailThreadMessage.ts b/packages/twenty-front/src/modules/activities/emails/types/EmailThreadMessage.ts index cd67340d67..07a910caf7 100644 --- a/packages/twenty-front/src/modules/activities/emails/types/EmailThreadMessage.ts +++ b/packages/twenty-front/src/modules/activities/emails/types/EmailThreadMessage.ts @@ -10,5 +10,6 @@ export type EmailThreadMessage = { messageThreadId: string; messageParticipants: EmailThreadMessageParticipant[]; messageThread: MessageThread; + isDraft: boolean; __typename: 'EmailThreadMessage'; }; diff --git a/packages/twenty-front/src/modules/activities/emails/utils/getEmailDraftPrefillFromMessage.ts b/packages/twenty-front/src/modules/activities/emails/utils/getEmailDraftPrefillFromMessage.ts new file mode 100644 index 0000000000..e6960c8157 --- /dev/null +++ b/packages/twenty-front/src/modules/activities/emails/utils/getEmailDraftPrefillFromMessage.ts @@ -0,0 +1,22 @@ +import { type EmailDraftPrefill } from '@/activities/emails/types/EmailDraftPrefill'; +import { type EmailThreadMessageWithSender } from '@/activities/emails/types/EmailThreadMessageWithSender'; +import { MessageParticipantRole } from 'twenty-shared/types'; + +export const getEmailDraftPrefillFromMessage = ( + message: EmailThreadMessageWithSender, +): EmailDraftPrefill => { + const joinHandlesByRole = (role: MessageParticipantRole) => + message.messageParticipants + .filter((participant) => participant.role === role) + .map((participant) => participant.handle) + .join(', '); + + return { + messageId: message.id, + to: joinHandlesByRole(MessageParticipantRole.TO), + cc: joinHandlesByRole(MessageParticipantRole.CC), + bcc: joinHandlesByRole(MessageParticipantRole.BCC), + subject: message.subject, + body: message.text, + }; +}; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/email-thread/components/EmailThreadComposer.tsx b/packages/twenty-front/src/modules/page-layout/widgets/email-thread/components/EmailThreadComposer.tsx index 2f6b410e19..cba202fe22 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/email-thread/components/EmailThreadComposer.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/email-thread/components/EmailThreadComposer.tsx @@ -4,11 +4,15 @@ import { useCallback, useMemo } from 'react'; import { EmailComposerFields } from '@/activities/emails/components/EmailComposerFields'; import { useEmailComposerState } from '@/activities/emails/hooks/useEmailComposerState'; import { type ReplyContextReady } from '@/activities/emails/hooks/useReplyContext'; +import { type EmailDraftPrefill } from '@/activities/emails/types/EmailDraftPrefill'; import { EmailThreadComposerFooterEffect } from '@/page-layout/widgets/email-thread/components/EmailThreadComposerFooterEffect'; import { SIDE_PANEL_FOCUS_ID } from '@/side-panel/constants/SidePanelFocusId'; +import { useOpenRecordInSidePanel } from '@/side-panel/hooks/useOpenRecordInSidePanel'; import { type SidePanelFooterCommandMenuItem } from '@/ui/layout/side-panel/types/SidePanelFooterCommandMenuItem'; import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement'; import { t } from '@lingui/core/macro'; +import { CoreObjectNameSingular } from 'twenty-shared/types'; +import { isDefined } from 'twenty-shared/utils'; import { IconArrowBackUp, IconSend, IconX } from 'twenty-ui/icon'; import { themeCssVariables } from 'twenty-ui/theme-constants'; import { getOsControlSymbol } from 'twenty-ui/utilities'; @@ -34,6 +38,7 @@ type EmailThreadComposerProps = { isInSidePanel: boolean; isComposerOpen: boolean; setIsComposerOpen: (open: boolean) => void; + draftPrefill?: EmailDraftPrefill | null; }; export const EmailThreadComposer = ({ @@ -41,13 +46,27 @@ export const EmailThreadComposer = ({ isInSidePanel, isComposerOpen, setIsComposerOpen, + draftPrefill, }: EmailThreadComposerProps) => { - const handleReplySent = useCallback(() => { - setIsComposerOpen(false); - }, [setIsComposerOpen]); + const { openRecordInSidePanel } = useOpenRecordInSidePanel(); + + const handleReplySent = useCallback( + (messageThreadId: string | null) => { + setIsComposerOpen(false); + + if (isDefined(messageThreadId)) { + openRecordInSidePanel({ + recordId: messageThreadId, + objectNameSingular: CoreObjectNameSingular.MessageThread, + }); + } + }, + [setIsComposerOpen, openRecordInSidePanel], + ); const composerState = useEmailComposerState({ connectedAccountId: replyContext.connectedAccountId, + draftPrefill, defaultTo: replyContext.to, defaultSubject: replyContext.subject, defaultInReplyTo: replyContext.inReplyTo, diff --git a/packages/twenty-front/src/modules/page-layout/widgets/email-thread/components/EmailThreadIntermediaryMessages.tsx b/packages/twenty-front/src/modules/page-layout/widgets/email-thread/components/EmailThreadIntermediaryMessages.tsx index f856c49220..02bcaa8be6 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/email-thread/components/EmailThreadIntermediaryMessages.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/email-thread/components/EmailThreadIntermediaryMessages.tsx @@ -15,8 +15,10 @@ const StyledButtonContainer = styled.div` export const EmailThreadIntermediaryMessages = ({ messages, + onDraftClick, }: { messages: EmailThreadMessageWithSender[]; + onDraftClick: (message: EmailThreadMessageWithSender) => void; }) => { const [areMessagesOpen, setAreMessagesOpen] = useState(false); const messagesLength = messages.length; @@ -29,10 +31,8 @@ export const EmailThreadIntermediaryMessages = ({ messages.map((message) => ( )) ) : ( diff --git a/packages/twenty-front/src/modules/page-layout/widgets/email-thread/components/EmailThreadWidget.tsx b/packages/twenty-front/src/modules/page-layout/widgets/email-thread/components/EmailThreadWidget.tsx index 657322d9ed..fd61caa930 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/email-thread/components/EmailThreadWidget.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/email-thread/components/EmailThreadWidget.tsx @@ -1,11 +1,14 @@ import { styled } from '@linaria/react'; -import { useState } from 'react'; +import { useCallback, useState } from 'react'; import { CustomResolverFetchMoreLoader } from '@/activities/components/CustomResolverFetchMoreLoader'; import { EmailLoader } from '@/activities/emails/components/EmailLoader'; import { EmailThreadMessage } from '@/activities/emails/components/EmailThreadMessage'; import { useEmailThread } from '@/activities/emails/hooks/useEmailThread'; import { useReplyContext } from '@/activities/emails/hooks/useReplyContext'; +import { type EmailDraftPrefill } from '@/activities/emails/types/EmailDraftPrefill'; +import { type EmailThreadMessageWithSender } from '@/activities/emails/types/EmailThreadMessageWithSender'; +import { getEmailDraftPrefillFromMessage } from '@/activities/emails/utils/getEmailDraftPrefillFromMessage'; import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget'; import { EmailThreadComposer } from '@/page-layout/widgets/email-thread/components/EmailThreadComposer'; import { EmailThreadIntermediaryMessages } from '@/page-layout/widgets/email-thread/components/EmailThreadIntermediaryMessages'; @@ -43,7 +46,36 @@ export const EmailThreadWidget = ({ const replyContext = useReplyContext(targetRecord.id); - const [isComposerOpen, setIsComposerOpen] = useState(false); + const [composerIntent, setComposerIntent] = useState< + 'opened' | 'closed' | null + >(null); + const [clickedDraftPrefill, setClickedDraftPrefill] = + useState(null); + const [previousTargetRecordId, setPreviousTargetRecordId] = useState( + targetRecord.id, + ); + + if (previousTargetRecordId !== targetRecord.id) { + setPreviousTargetRecordId(targetRecord.id); + setComposerIntent(null); + setClickedDraftPrefill(null); + } + + const handleComposerOpenChange = useCallback((open: boolean) => { + setComposerIntent(open ? 'opened' : 'closed'); + + if (!open) { + setClickedDraftPrefill(null); + } + }, []); + + const handleDraftClick = useCallback( + (message: EmailThreadMessageWithSender) => { + setClickedDraftPrefill(getEmailDraftPrefillFromMessage(message)); + setComposerIntent('opened'); + }, + [], + ); const canReply = isDefined(replyContext) && !replyContext.loading; @@ -58,6 +90,16 @@ export const EmailThreadWidget = ({ : []; const lastMessage = messages[messagesCount - 1]; + const trailingDraft = lastMessage?.isDraft ? lastMessage : undefined; + const draftPrefill = + clickedDraftPrefill ?? + (isDefined(trailingDraft) + ? getEmailDraftPrefillFromMessage(trailingDraft) + : null); + const isComposerOpen = + composerIntent === 'opened' || + (composerIntent === null && isDefined(trailingDraft)); + if (threadLoading || !thread || !messages.length) { return ( @@ -74,21 +116,20 @@ export const EmailThreadWidget = ({ {firstMessages.map((message) => ( ))} - + {canReply && ( )} diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/getInitialAdvancedTextEditorContent.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/getInitialAdvancedTextEditorContent.ts index dda5907e48..d6b199c03c 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/getInitialAdvancedTextEditorContent.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/getInitialAdvancedTextEditorContent.ts @@ -1,6 +1,5 @@ import { getInitialEditorContent } from '@/workflow/workflow-variables/utils/getInitialEditorContent'; import type { JSONContent } from '@tiptap/react'; -import { logError } from '~/utils/logError'; // Previous format of the email body was plain text, // but from now on we will save it as JSON. @@ -33,8 +32,7 @@ export const getInitialAdvancedTextEditorContent = ( } return json; - } catch (error) { - logError(error); + } catch { return getInitialEditorContent(rawContent); } }; diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-18/2-18-upgrade-version-command.module.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-18/2-18-upgrade-version-command.module.ts index e5631e6beb..515548774f 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/2-18/2-18-upgrade-version-command.module.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-18/2-18-upgrade-version-command.module.ts @@ -1,16 +1,21 @@ import { Module } from '@nestjs/common'; import { WorkspaceIteratorModule } from 'src/database/commands/command-runners/workspace-iterator.module'; +import { AddMessageIsDraftFieldCommand } from 'src/database/commands/upgrade-version-command/2-18/2-18-workspace-command-1810000005000-add-message-is-draft-field.command'; import { NormalizeLegacyIndexNamesCommand } from 'src/database/commands/upgrade-version-command/2-18/2-18-workspace-command-1799200000000-normalize-legacy-index-names.command'; +import { ApplicationModule } from 'src/engine/core-modules/application/application.module'; import { WorkspaceSchemaManagerModule } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.module'; import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module'; +import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace-migration/workspace-migration.module'; @Module({ imports: [ + ApplicationModule, WorkspaceCacheModule, WorkspaceIteratorModule, + WorkspaceMigrationModule, WorkspaceSchemaManagerModule, ], - providers: [NormalizeLegacyIndexNamesCommand], + providers: [AddMessageIsDraftFieldCommand, NormalizeLegacyIndexNamesCommand], }) export class V2_18_UpgradeVersionCommandModule {} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-18/2-18-workspace-command-1810000005000-add-message-is-draft-field.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-18/2-18-workspace-command-1810000005000-add-message-is-draft-field.command.ts new file mode 100644 index 0000000000..7b80a4ba97 --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-18/2-18-workspace-command-1810000005000-add-message-is-draft-field.command.ts @@ -0,0 +1,144 @@ +import { Command } from 'nest-commander'; +import { isDefined } from 'twenty-shared/utils'; + +import { ActiveOrSuspendedWorkspaceCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspace.command-runner'; +import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service'; +import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner'; +import { ApplicationService } from 'src/engine/core-modules/application/application.service'; +import { RegisteredWorkspaceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-workspace-command.decorator'; +import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util'; +import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type'; +import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type'; +import { computeTwentyStandardApplicationAllFlatEntityMaps } from 'src/engine/workspace-manager/twenty-standard-application/utils/twenty-standard-application-all-flat-entity-maps.constant'; +import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service'; +import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration/services/workspace-migration-validate-build-and-run-service'; + +const MESSAGE_OBJECT_UNIVERSAL_IDENTIFIER = + '20202020-3f6b-4425-80ab-e468899ab4b2'; +const MESSAGE_IS_DRAFT_FIELD_UNIVERSAL_IDENTIFIER = + '20202020-4d3a-4b6e-9c1f-2a5e7b9d0c34'; + +@RegisteredWorkspaceCommand('2.18.0', 1810000005000) +@Command({ + name: 'upgrade:2-18:add-message-is-draft-field', + description: + 'Add the Message isDraft field metadata and column to existing workspaces', +}) +export class AddMessageIsDraftFieldCommand extends ActiveOrSuspendedWorkspaceCommandRunner { + constructor( + protected readonly workspaceIteratorService: WorkspaceIteratorService, + private readonly applicationService: ApplicationService, + private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService, + private readonly workspaceCacheService: WorkspaceCacheService, + ) { + super(workspaceIteratorService); + } + + override async runOnWorkspace({ + workspaceId, + options, + }: RunOnWorkspaceArgs): Promise { + const isDryRun = options.dryRun ?? false; + + const { flatFieldMetadataMaps, flatObjectMetadataMaps } = + await this.workspaceCacheService.getOrRecompute(workspaceId, [ + 'flatFieldMetadataMaps', + 'flatObjectMetadataMaps', + ]); + + const messageObjectMetadata = + findFlatEntityByUniversalIdentifier({ + flatEntityMaps: flatObjectMetadataMaps, + universalIdentifier: MESSAGE_OBJECT_UNIVERSAL_IDENTIFIER, + }); + + if (!isDefined(messageObjectMetadata)) { + this.logger.log( + `Message object does not exist for workspace ${workspaceId}, skipping`, + ); + + return; + } + + const existingIsDraftFieldMetadata = + findFlatEntityByUniversalIdentifier({ + flatEntityMaps: flatFieldMetadataMaps, + universalIdentifier: MESSAGE_IS_DRAFT_FIELD_UNIVERSAL_IDENTIFIER, + }); + + if (isDefined(existingIsDraftFieldMetadata)) { + this.logger.log( + `Message isDraft field already present for workspace ${workspaceId}`, + ); + + return; + } + + const { twentyStandardFlatApplication } = + await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( + { workspaceId }, + ); + + const { allFlatEntityMaps: standardAllFlatEntityMaps } = + computeTwentyStandardApplicationAllFlatEntityMaps({ + now: new Date().toISOString(), + workspaceId, + twentyStandardApplicationId: twentyStandardFlatApplication.id, + }); + + const isDraftFlatFieldMetadata = + findFlatEntityByUniversalIdentifier({ + flatEntityMaps: standardAllFlatEntityMaps.flatFieldMetadataMaps, + universalIdentifier: MESSAGE_IS_DRAFT_FIELD_UNIVERSAL_IDENTIFIER, + }); + + if (!isDefined(isDraftFlatFieldMetadata)) { + throw new Error( + 'Standard application is missing the Message isDraft field metadata', + ); + } + + if (isDryRun) { + this.logger.log( + `[DRY RUN] Would create Message isDraft field for workspace ${workspaceId}`, + ); + + return; + } + + const validateAndBuildResult = + await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration( + { + isSystemBuild: true, + allFlatEntityOperationByMetadataName: { + fieldMetadata: { + flatEntityToCreate: [isDraftFlatFieldMetadata], + flatEntityToDelete: [], + flatEntityToUpdate: [], + }, + }, + workspaceId, + applicationUniversalIdentifier: + twentyStandardFlatApplication.universalIdentifier, + }, + ); + + if (validateAndBuildResult.status === 'fail') { + this.logger.error( + `Failed to create Message isDraft field:\n${JSON.stringify( + validateAndBuildResult, + null, + 2, + )}`, + ); + + throw new Error( + `Failed to create Message isDraft field for workspace ${workspaceId}`, + ); + } + + this.logger.log( + `Created Message isDraft field for workspace ${workspaceId}`, + ); + } +} diff --git a/packages/twenty-server/src/engine/core-modules/messaging/dtos/timeline-thread.dto.ts b/packages/twenty-server/src/engine/core-modules/messaging/dtos/timeline-thread.dto.ts index 04b9abbbd8..d6c4c1cdd3 100644 --- a/packages/twenty-server/src/engine/core-modules/messaging/dtos/timeline-thread.dto.ts +++ b/packages/twenty-server/src/engine/core-modules/messaging/dtos/timeline-thread.dto.ts @@ -35,4 +35,7 @@ export class TimelineThreadDTO { @Field() participantCount: number; + + @Field() + lastMessageIsDraft: boolean; } diff --git a/packages/twenty-server/src/engine/core-modules/messaging/services/timeline-messaging.service.ts b/packages/twenty-server/src/engine/core-modules/messaging/services/timeline-messaging.service.ts index f3535db298..ed2f578fdd 100644 --- a/packages/twenty-server/src/engine/core-modules/messaging/services/timeline-messaging.service.ts +++ b/packages/twenty-server/src/engine/core-modules/messaging/services/timeline-messaging.service.ts @@ -106,6 +106,7 @@ export class TimelineMessagingService { lastMessageBody: lastMessage.text ?? '', lastMessageReceivedAt: lastMessage.receivedAt ?? new Date(), numberOfMessagesInThread: messageThread.messages.length, + lastMessageIsDraft: lastMessage.isDraft ?? false, }; }), totalNumberOfThreads, diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/__tests__/__snapshots__/get-standard-object-metadata-related-entity-ids.util.spec.ts.snap b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/__tests__/__snapshots__/get-standard-object-metadata-related-entity-ids.util.spec.ts.snap index 31287b3588..1bd4919d98 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/__tests__/__snapshots__/get-standard-object-metadata-related-entity-ids.util.spec.ts.snap +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/__tests__/__snapshots__/get-standard-object-metadata-related-entity-ids.util.spec.ts.snap @@ -909,7 +909,7 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "id": "00000000-0000-0000-0000-000000000418", }, "createdBy": { - "id": "00000000-0000-0000-0000-000000000430", + "id": "00000000-0000-0000-0000-000000000431", }, "deletedAt": { "id": "00000000-0000-0000-0000-000000000420", @@ -923,6 +923,9 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "id": { "id": "00000000-0000-0000-0000-000000000417", }, + "isDraft": { + "id": "00000000-0000-0000-0000-000000000430", + }, "messageCampaign": { "id": "00000000-0000-0000-0000-000000000428", }, @@ -936,13 +939,13 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "id": "00000000-0000-0000-0000-000000000422", }, "position": { - "id": "00000000-0000-0000-0000-000000000432", + "id": "00000000-0000-0000-0000-000000000433", }, "receivedAt": { "id": "00000000-0000-0000-0000-000000000425", }, "searchVector": { - "id": "00000000-0000-0000-0000-000000000433", + "id": "00000000-0000-0000-0000-000000000434", }, "subject": { "id": "00000000-0000-0000-0000-000000000423", @@ -954,35 +957,35 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "id": "00000000-0000-0000-0000-000000000419", }, "updatedBy": { - "id": "00000000-0000-0000-0000-000000000431", + "id": "00000000-0000-0000-0000-000000000432", }, }, - "id": "00000000-0000-0000-0000-000000000442", + "id": "00000000-0000-0000-0000-000000000443", "views": { "allMessages": { - "id": "00000000-0000-0000-0000-000000000441", + "id": "00000000-0000-0000-0000-000000000442", "viewFieldGroups": {}, "viewFields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000440", + "id": "00000000-0000-0000-0000-000000000441", }, "headerMessageId": { - "id": "00000000-0000-0000-0000-000000000438", + "id": "00000000-0000-0000-0000-000000000439", }, "messageParticipants": { - "id": "00000000-0000-0000-0000-000000000436", - }, - "messageThread": { - "id": "00000000-0000-0000-0000-000000000435", - }, - "receivedAt": { "id": "00000000-0000-0000-0000-000000000437", }, + "messageThread": { + "id": "00000000-0000-0000-0000-000000000436", + }, + "receivedAt": { + "id": "00000000-0000-0000-0000-000000000438", + }, "subject": { - "id": "00000000-0000-0000-0000-000000000434", + "id": "00000000-0000-0000-0000-000000000435", }, "text": { - "id": "00000000-0000-0000-0000-000000000439", + "id": "00000000-0000-0000-0000-000000000440", }, }, "viewGroups": {}, @@ -1489,104 +1492,104 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "note": { "fields": { "attachments": { - "id": "00000000-0000-0000-0000-000000000453", - }, - "bodyV2": { - "id": "00000000-0000-0000-0000-000000000449", - }, - "createdAt": { - "id": "00000000-0000-0000-0000-000000000444", - }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000450", - }, - "deletedAt": { - "id": "00000000-0000-0000-0000-000000000446", - }, - "id": { - "id": "00000000-0000-0000-0000-000000000443", - }, - "noteTargets": { - "id": "00000000-0000-0000-0000-000000000452", - }, - "position": { - "id": "00000000-0000-0000-0000-000000000447", - }, - "searchVector": { - "id": "00000000-0000-0000-0000-000000000455", - }, - "timelineActivities": { "id": "00000000-0000-0000-0000-000000000454", }, - "title": { - "id": "00000000-0000-0000-0000-000000000448", + "bodyV2": { + "id": "00000000-0000-0000-0000-000000000450", }, - "updatedAt": { + "createdAt": { "id": "00000000-0000-0000-0000-000000000445", }, - "updatedBy": { + "createdBy": { "id": "00000000-0000-0000-0000-000000000451", }, + "deletedAt": { + "id": "00000000-0000-0000-0000-000000000447", + }, + "id": { + "id": "00000000-0000-0000-0000-000000000444", + }, + "noteTargets": { + "id": "00000000-0000-0000-0000-000000000453", + }, + "position": { + "id": "00000000-0000-0000-0000-000000000448", + }, + "searchVector": { + "id": "00000000-0000-0000-0000-000000000456", + }, + "timelineActivities": { + "id": "00000000-0000-0000-0000-000000000455", + }, + "title": { + "id": "00000000-0000-0000-0000-000000000449", + }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000446", + }, + "updatedBy": { + "id": "00000000-0000-0000-0000-000000000452", + }, }, - "id": "00000000-0000-0000-0000-000000000473", + "id": "00000000-0000-0000-0000-000000000474", "views": { "allNotes": { - "id": "00000000-0000-0000-0000-000000000461", + "id": "00000000-0000-0000-0000-000000000462", "viewFieldGroups": {}, "viewFields": { "bodyV2": { - "id": "00000000-0000-0000-0000-000000000458", - }, - "createdAt": { - "id": "00000000-0000-0000-0000-000000000460", - }, - "createdBy": { "id": "00000000-0000-0000-0000-000000000459", }, + "createdAt": { + "id": "00000000-0000-0000-0000-000000000461", + }, + "createdBy": { + "id": "00000000-0000-0000-0000-000000000460", + }, "noteTargets": { - "id": "00000000-0000-0000-0000-000000000457", + "id": "00000000-0000-0000-0000-000000000458", }, "title": { - "id": "00000000-0000-0000-0000-000000000456", + "id": "00000000-0000-0000-0000-000000000457", }, }, "viewGroups": {}, }, "noteRecordPageFields": { - "id": "00000000-0000-0000-0000-000000000472", + "id": "00000000-0000-0000-0000-000000000473", "viewFieldGroups": { "general": { - "id": "00000000-0000-0000-0000-000000000470", + "id": "00000000-0000-0000-0000-000000000471", }, "system": { - "id": "00000000-0000-0000-0000-000000000471", + "id": "00000000-0000-0000-0000-000000000472", }, }, "viewFields": { "attachments": { - "id": "00000000-0000-0000-0000-000000000468", - }, - "bodyV2": { - "id": "00000000-0000-0000-0000-000000000465", - }, - "createdAt": { - "id": "00000000-0000-0000-0000-000000000462", - }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000463", - }, - "noteTargets": { - "id": "00000000-0000-0000-0000-000000000464", - }, - "timelineActivities": { "id": "00000000-0000-0000-0000-000000000469", }, - "updatedAt": { + "bodyV2": { "id": "00000000-0000-0000-0000-000000000466", }, - "updatedBy": { + "createdAt": { + "id": "00000000-0000-0000-0000-000000000463", + }, + "createdBy": { + "id": "00000000-0000-0000-0000-000000000464", + }, + "noteTargets": { + "id": "00000000-0000-0000-0000-000000000465", + }, + "timelineActivities": { + "id": "00000000-0000-0000-0000-000000000470", + }, + "updatedAt": { "id": "00000000-0000-0000-0000-000000000467", }, + "updatedBy": { + "id": "00000000-0000-0000-0000-000000000468", + }, }, "viewGroups": {}, }, @@ -1595,62 +1598,62 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "noteTarget": { "fields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000475", - }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000482", - }, - "deletedAt": { - "id": "00000000-0000-0000-0000-000000000477", - }, - "id": { - "id": "00000000-0000-0000-0000-000000000474", - }, - "note": { - "id": "00000000-0000-0000-0000-000000000478", - }, - "position": { - "id": "00000000-0000-0000-0000-000000000484", - }, - "searchVector": { - "id": "00000000-0000-0000-0000-000000000485", - }, - "targetCompany": { - "id": "00000000-0000-0000-0000-000000000480", - }, - "targetOpportunity": { - "id": "00000000-0000-0000-0000-000000000481", - }, - "targetPerson": { - "id": "00000000-0000-0000-0000-000000000479", - }, - "updatedAt": { "id": "00000000-0000-0000-0000-000000000476", }, - "updatedBy": { + "createdBy": { "id": "00000000-0000-0000-0000-000000000483", }, + "deletedAt": { + "id": "00000000-0000-0000-0000-000000000478", + }, + "id": { + "id": "00000000-0000-0000-0000-000000000475", + }, + "note": { + "id": "00000000-0000-0000-0000-000000000479", + }, + "position": { + "id": "00000000-0000-0000-0000-000000000485", + }, + "searchVector": { + "id": "00000000-0000-0000-0000-000000000486", + }, + "targetCompany": { + "id": "00000000-0000-0000-0000-000000000481", + }, + "targetOpportunity": { + "id": "00000000-0000-0000-0000-000000000482", + }, + "targetPerson": { + "id": "00000000-0000-0000-0000-000000000480", + }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000477", + }, + "updatedBy": { + "id": "00000000-0000-0000-0000-000000000484", + }, }, - "id": "00000000-0000-0000-0000-000000000492", + "id": "00000000-0000-0000-0000-000000000493", "views": { "allNoteTargets": { - "id": "00000000-0000-0000-0000-000000000491", + "id": "00000000-0000-0000-0000-000000000492", "viewFieldGroups": {}, "viewFields": { "id": { - "id": "00000000-0000-0000-0000-000000000486", - }, - "note": { "id": "00000000-0000-0000-0000-000000000487", }, - "targetCompany": { - "id": "00000000-0000-0000-0000-000000000489", + "note": { + "id": "00000000-0000-0000-0000-000000000488", }, - "targetOpportunity": { + "targetCompany": { "id": "00000000-0000-0000-0000-000000000490", }, + "targetOpportunity": { + "id": "00000000-0000-0000-0000-000000000491", + }, "targetPerson": { - "id": "00000000-0000-0000-0000-000000000488", + "id": "00000000-0000-0000-0000-000000000489", }, }, "viewGroups": {}, @@ -1660,187 +1663,187 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "opportunity": { "fields": { "amount": { - "id": "00000000-0000-0000-0000-000000000498", - }, - "attachments": { - "id": "00000000-0000-0000-0000-000000000509", - }, - "closeDate": { "id": "00000000-0000-0000-0000-000000000499", }, - "company": { - "id": "00000000-0000-0000-0000-000000000505", - }, - "createdAt": { - "id": "00000000-0000-0000-0000-000000000494", - }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000502", - }, - "deletedAt": { - "id": "00000000-0000-0000-0000-000000000496", - }, - "id": { - "id": "00000000-0000-0000-0000-000000000493", - }, - "name": { - "id": "00000000-0000-0000-0000-000000000497", - }, - "noteTargets": { - "id": "00000000-0000-0000-0000-000000000508", - }, - "owner": { - "id": "00000000-0000-0000-0000-000000000506", - }, - "pointOfContact": { - "id": "00000000-0000-0000-0000-000000000504", - }, - "position": { - "id": "00000000-0000-0000-0000-000000000501", - }, - "searchVector": { - "id": "00000000-0000-0000-0000-000000000511", - }, - "stage": { - "id": "00000000-0000-0000-0000-000000000500", - }, - "taskTargets": { - "id": "00000000-0000-0000-0000-000000000507", - }, - "timelineActivities": { + "attachments": { "id": "00000000-0000-0000-0000-000000000510", }, - "updatedAt": { + "closeDate": { + "id": "00000000-0000-0000-0000-000000000500", + }, + "company": { + "id": "00000000-0000-0000-0000-000000000506", + }, + "createdAt": { "id": "00000000-0000-0000-0000-000000000495", }, - "updatedBy": { + "createdBy": { "id": "00000000-0000-0000-0000-000000000503", }, + "deletedAt": { + "id": "00000000-0000-0000-0000-000000000497", + }, + "id": { + "id": "00000000-0000-0000-0000-000000000494", + }, + "name": { + "id": "00000000-0000-0000-0000-000000000498", + }, + "noteTargets": { + "id": "00000000-0000-0000-0000-000000000509", + }, + "owner": { + "id": "00000000-0000-0000-0000-000000000507", + }, + "pointOfContact": { + "id": "00000000-0000-0000-0000-000000000505", + }, + "position": { + "id": "00000000-0000-0000-0000-000000000502", + }, + "searchVector": { + "id": "00000000-0000-0000-0000-000000000512", + }, + "stage": { + "id": "00000000-0000-0000-0000-000000000501", + }, + "taskTargets": { + "id": "00000000-0000-0000-0000-000000000508", + }, + "timelineActivities": { + "id": "00000000-0000-0000-0000-000000000511", + }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000496", + }, + "updatedBy": { + "id": "00000000-0000-0000-0000-000000000504", + }, }, - "id": "00000000-0000-0000-0000-000000000549", + "id": "00000000-0000-0000-0000-000000000550", "views": { "allOpportunities": { - "id": "00000000-0000-0000-0000-000000000518", + "id": "00000000-0000-0000-0000-000000000519", "viewFieldGroups": {}, "viewFields": { "amount": { - "id": "00000000-0000-0000-0000-000000000513", - }, - "closeDate": { - "id": "00000000-0000-0000-0000-000000000515", - }, - "company": { - "id": "00000000-0000-0000-0000-000000000516", - }, - "createdBy": { "id": "00000000-0000-0000-0000-000000000514", }, + "closeDate": { + "id": "00000000-0000-0000-0000-000000000516", + }, + "company": { + "id": "00000000-0000-0000-0000-000000000517", + }, + "createdBy": { + "id": "00000000-0000-0000-0000-000000000515", + }, "name": { - "id": "00000000-0000-0000-0000-000000000512", + "id": "00000000-0000-0000-0000-000000000513", }, "pointOfContact": { - "id": "00000000-0000-0000-0000-000000000517", + "id": "00000000-0000-0000-0000-000000000518", }, }, "viewGroups": {}, }, "byStage": { - "id": "00000000-0000-0000-0000-000000000530", + "id": "00000000-0000-0000-0000-000000000531", "viewFieldGroups": {}, "viewFields": { "amount": { - "id": "00000000-0000-0000-0000-000000000520", - }, - "closeDate": { - "id": "00000000-0000-0000-0000-000000000522", - }, - "company": { - "id": "00000000-0000-0000-0000-000000000523", - }, - "createdBy": { "id": "00000000-0000-0000-0000-000000000521", }, + "closeDate": { + "id": "00000000-0000-0000-0000-000000000523", + }, + "company": { + "id": "00000000-0000-0000-0000-000000000524", + }, + "createdBy": { + "id": "00000000-0000-0000-0000-000000000522", + }, "name": { - "id": "00000000-0000-0000-0000-000000000519", + "id": "00000000-0000-0000-0000-000000000520", }, "pointOfContact": { - "id": "00000000-0000-0000-0000-000000000524", + "id": "00000000-0000-0000-0000-000000000525", }, }, "viewGroups": { "customer": { - "id": "00000000-0000-0000-0000-000000000529", + "id": "00000000-0000-0000-0000-000000000530", }, "meeting": { - "id": "00000000-0000-0000-0000-000000000527", - }, - "new": { - "id": "00000000-0000-0000-0000-000000000525", - }, - "proposal": { "id": "00000000-0000-0000-0000-000000000528", }, - "screening": { + "new": { "id": "00000000-0000-0000-0000-000000000526", }, + "proposal": { + "id": "00000000-0000-0000-0000-000000000529", + }, + "screening": { + "id": "00000000-0000-0000-0000-000000000527", + }, }, }, "opportunityRecordPageFields": { - "id": "00000000-0000-0000-0000-000000000548", + "id": "00000000-0000-0000-0000-000000000549", "viewFieldGroups": { "deal": { - "id": "00000000-0000-0000-0000-000000000545", - }, - "relations": { "id": "00000000-0000-0000-0000-000000000546", }, - "system": { + "relations": { "id": "00000000-0000-0000-0000-000000000547", }, + "system": { + "id": "00000000-0000-0000-0000-000000000548", + }, }, "viewFields": { "amount": { - "id": "00000000-0000-0000-0000-000000000531", - }, - "attachments": { - "id": "00000000-0000-0000-0000-000000000543", - }, - "closeDate": { "id": "00000000-0000-0000-0000-000000000532", }, - "company": { - "id": "00000000-0000-0000-0000-000000000534", - }, - "createdAt": { - "id": "00000000-0000-0000-0000-000000000537", - }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000538", - }, - "noteTargets": { - "id": "00000000-0000-0000-0000-000000000542", - }, - "owner": { - "id": "00000000-0000-0000-0000-000000000536", - }, - "pointOfContact": { - "id": "00000000-0000-0000-0000-000000000535", - }, - "stage": { - "id": "00000000-0000-0000-0000-000000000533", - }, - "taskTargets": { - "id": "00000000-0000-0000-0000-000000000541", - }, - "timelineActivities": { + "attachments": { "id": "00000000-0000-0000-0000-000000000544", }, - "updatedAt": { + "closeDate": { + "id": "00000000-0000-0000-0000-000000000533", + }, + "company": { + "id": "00000000-0000-0000-0000-000000000535", + }, + "createdAt": { + "id": "00000000-0000-0000-0000-000000000538", + }, + "createdBy": { "id": "00000000-0000-0000-0000-000000000539", }, - "updatedBy": { + "noteTargets": { + "id": "00000000-0000-0000-0000-000000000543", + }, + "owner": { + "id": "00000000-0000-0000-0000-000000000537", + }, + "pointOfContact": { + "id": "00000000-0000-0000-0000-000000000536", + }, + "stage": { + "id": "00000000-0000-0000-0000-000000000534", + }, + "taskTargets": { + "id": "00000000-0000-0000-0000-000000000542", + }, + "timelineActivities": { + "id": "00000000-0000-0000-0000-000000000545", + }, + "updatedAt": { "id": "00000000-0000-0000-0000-000000000540", }, + "updatedBy": { + "id": "00000000-0000-0000-0000-000000000541", + }, }, "viewGroups": {}, }, @@ -1849,182 +1852,182 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "person": { "fields": { "attachments": { - "id": "00000000-0000-0000-0000-000000000568", - }, - "avatarFile": { - "id": "00000000-0000-0000-0000-000000000560", - }, - "avatarUrl": { - "id": "00000000-0000-0000-0000-000000000559", - }, - "calendarEventParticipants": { - "id": "00000000-0000-0000-0000-000000000570", - }, - "company": { - "id": "00000000-0000-0000-0000-000000000564", - }, - "createdAt": { - "id": "00000000-0000-0000-0000-000000000551", - }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000562", - }, - "deletedAt": { - "id": "00000000-0000-0000-0000-000000000553", - }, - "emails": { - "id": "00000000-0000-0000-0000-000000000555", - }, - "id": { - "id": "00000000-0000-0000-0000-000000000550", - }, - "jobTitle": { - "id": "00000000-0000-0000-0000-000000000557", - }, - "linkedinLink": { - "id": "00000000-0000-0000-0000-000000000556", - }, - "listMemberships": { - "id": "00000000-0000-0000-0000-000000000572", - }, - "messageParticipants": { "id": "00000000-0000-0000-0000-000000000569", }, - "name": { - "id": "00000000-0000-0000-0000-000000000554", - }, - "noteTargets": { - "id": "00000000-0000-0000-0000-000000000567", - }, - "phones": { - "id": "00000000-0000-0000-0000-000000000558", - }, - "pointOfContactForOpportunities": { - "id": "00000000-0000-0000-0000-000000000565", - }, - "position": { + "avatarFile": { "id": "00000000-0000-0000-0000-000000000561", }, - "searchVector": { - "id": "00000000-0000-0000-0000-000000000573", + "avatarUrl": { + "id": "00000000-0000-0000-0000-000000000560", }, - "taskTargets": { - "id": "00000000-0000-0000-0000-000000000566", - }, - "timelineActivities": { + "calendarEventParticipants": { "id": "00000000-0000-0000-0000-000000000571", }, - "updatedAt": { + "company": { + "id": "00000000-0000-0000-0000-000000000565", + }, + "createdAt": { "id": "00000000-0000-0000-0000-000000000552", }, - "updatedBy": { + "createdBy": { "id": "00000000-0000-0000-0000-000000000563", }, + "deletedAt": { + "id": "00000000-0000-0000-0000-000000000554", + }, + "emails": { + "id": "00000000-0000-0000-0000-000000000556", + }, + "id": { + "id": "00000000-0000-0000-0000-000000000551", + }, + "jobTitle": { + "id": "00000000-0000-0000-0000-000000000558", + }, + "linkedinLink": { + "id": "00000000-0000-0000-0000-000000000557", + }, + "listMemberships": { + "id": "00000000-0000-0000-0000-000000000573", + }, + "messageParticipants": { + "id": "00000000-0000-0000-0000-000000000570", + }, + "name": { + "id": "00000000-0000-0000-0000-000000000555", + }, + "noteTargets": { + "id": "00000000-0000-0000-0000-000000000568", + }, + "phones": { + "id": "00000000-0000-0000-0000-000000000559", + }, + "pointOfContactForOpportunities": { + "id": "00000000-0000-0000-0000-000000000566", + }, + "position": { + "id": "00000000-0000-0000-0000-000000000562", + }, + "searchVector": { + "id": "00000000-0000-0000-0000-000000000574", + }, + "taskTargets": { + "id": "00000000-0000-0000-0000-000000000567", + }, + "timelineActivities": { + "id": "00000000-0000-0000-0000-000000000572", + }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000553", + }, + "updatedBy": { + "id": "00000000-0000-0000-0000-000000000564", + }, }, - "id": "00000000-0000-0000-0000-000000000606", + "id": "00000000-0000-0000-0000-000000000607", "views": { "allPeople": { - "id": "00000000-0000-0000-0000-000000000582", + "id": "00000000-0000-0000-0000-000000000583", "viewFieldGroups": {}, "viewFields": { "company": { - "id": "00000000-0000-0000-0000-000000000577", + "id": "00000000-0000-0000-0000-000000000578", }, "createdAt": { - "id": "00000000-0000-0000-0000-000000000579", - }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000576", - }, - "emails": { - "id": "00000000-0000-0000-0000-000000000575", - }, - "jobTitle": { "id": "00000000-0000-0000-0000-000000000580", }, - "linkedinLink": { + "createdBy": { + "id": "00000000-0000-0000-0000-000000000577", + }, + "emails": { + "id": "00000000-0000-0000-0000-000000000576", + }, + "jobTitle": { "id": "00000000-0000-0000-0000-000000000581", }, + "linkedinLink": { + "id": "00000000-0000-0000-0000-000000000582", + }, "name": { - "id": "00000000-0000-0000-0000-000000000574", + "id": "00000000-0000-0000-0000-000000000575", }, "phones": { - "id": "00000000-0000-0000-0000-000000000578", + "id": "00000000-0000-0000-0000-000000000579", }, }, "viewGroups": {}, }, "personRecordPageFields": { - "id": "00000000-0000-0000-0000-000000000605", + "id": "00000000-0000-0000-0000-000000000606", "viewFieldGroups": { "general": { - "id": "00000000-0000-0000-0000-000000000601", + "id": "00000000-0000-0000-0000-000000000602", }, "social": { - "id": "00000000-0000-0000-0000-000000000603", - }, - "system": { "id": "00000000-0000-0000-0000-000000000604", }, + "system": { + "id": "00000000-0000-0000-0000-000000000605", + }, "work": { - "id": "00000000-0000-0000-0000-000000000602", + "id": "00000000-0000-0000-0000-000000000603", }, }, "viewFields": { "attachments": { - "id": "00000000-0000-0000-0000-000000000597", - }, - "avatarFile": { - "id": "00000000-0000-0000-0000-000000000593", - }, - "avatarUrl": { - "id": "00000000-0000-0000-0000-000000000588", - }, - "calendarEventParticipants": { - "id": "00000000-0000-0000-0000-000000000599", - }, - "company": { - "id": "00000000-0000-0000-0000-000000000585", - }, - "createdAt": { - "id": "00000000-0000-0000-0000-000000000589", - }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000590", - }, - "emails": { - "id": "00000000-0000-0000-0000-000000000583", - }, - "jobTitle": { - "id": "00000000-0000-0000-0000-000000000586", - }, - "linkedinLink": { - "id": "00000000-0000-0000-0000-000000000587", - }, - "messageParticipants": { "id": "00000000-0000-0000-0000-000000000598", }, - "noteTargets": { - "id": "00000000-0000-0000-0000-000000000596", - }, - "phones": { - "id": "00000000-0000-0000-0000-000000000584", - }, - "pointOfContactForOpportunities": { + "avatarFile": { "id": "00000000-0000-0000-0000-000000000594", }, - "taskTargets": { - "id": "00000000-0000-0000-0000-000000000595", + "avatarUrl": { + "id": "00000000-0000-0000-0000-000000000589", }, - "timelineActivities": { + "calendarEventParticipants": { "id": "00000000-0000-0000-0000-000000000600", }, - "updatedAt": { + "company": { + "id": "00000000-0000-0000-0000-000000000586", + }, + "createdAt": { + "id": "00000000-0000-0000-0000-000000000590", + }, + "createdBy": { "id": "00000000-0000-0000-0000-000000000591", }, - "updatedBy": { + "emails": { + "id": "00000000-0000-0000-0000-000000000584", + }, + "jobTitle": { + "id": "00000000-0000-0000-0000-000000000587", + }, + "linkedinLink": { + "id": "00000000-0000-0000-0000-000000000588", + }, + "messageParticipants": { + "id": "00000000-0000-0000-0000-000000000599", + }, + "noteTargets": { + "id": "00000000-0000-0000-0000-000000000597", + }, + "phones": { + "id": "00000000-0000-0000-0000-000000000585", + }, + "pointOfContactForOpportunities": { + "id": "00000000-0000-0000-0000-000000000595", + }, + "taskTargets": { + "id": "00000000-0000-0000-0000-000000000596", + }, + "timelineActivities": { + "id": "00000000-0000-0000-0000-000000000601", + }, + "updatedAt": { "id": "00000000-0000-0000-0000-000000000592", }, + "updatedBy": { + "id": "00000000-0000-0000-0000-000000000593", + }, }, "viewGroups": {}, }, @@ -2033,204 +2036,204 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "task": { "fields": { "assignee": { - "id": "00000000-0000-0000-0000-000000000620", - }, - "attachments": { - "id": "00000000-0000-0000-0000-000000000619", - }, - "bodyV2": { - "id": "00000000-0000-0000-0000-000000000613", - }, - "createdAt": { - "id": "00000000-0000-0000-0000-000000000608", - }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000616", - }, - "deletedAt": { - "id": "00000000-0000-0000-0000-000000000610", - }, - "dueAt": { - "id": "00000000-0000-0000-0000-000000000614", - }, - "id": { - "id": "00000000-0000-0000-0000-000000000607", - }, - "position": { - "id": "00000000-0000-0000-0000-000000000611", - }, - "searchVector": { - "id": "00000000-0000-0000-0000-000000000622", - }, - "status": { - "id": "00000000-0000-0000-0000-000000000615", - }, - "taskTargets": { - "id": "00000000-0000-0000-0000-000000000618", - }, - "timelineActivities": { "id": "00000000-0000-0000-0000-000000000621", }, - "title": { - "id": "00000000-0000-0000-0000-000000000612", + "attachments": { + "id": "00000000-0000-0000-0000-000000000620", }, - "updatedAt": { + "bodyV2": { + "id": "00000000-0000-0000-0000-000000000614", + }, + "createdAt": { "id": "00000000-0000-0000-0000-000000000609", }, - "updatedBy": { + "createdBy": { "id": "00000000-0000-0000-0000-000000000617", }, + "deletedAt": { + "id": "00000000-0000-0000-0000-000000000611", + }, + "dueAt": { + "id": "00000000-0000-0000-0000-000000000615", + }, + "id": { + "id": "00000000-0000-0000-0000-000000000608", + }, + "position": { + "id": "00000000-0000-0000-0000-000000000612", + }, + "searchVector": { + "id": "00000000-0000-0000-0000-000000000623", + }, + "status": { + "id": "00000000-0000-0000-0000-000000000616", + }, + "taskTargets": { + "id": "00000000-0000-0000-0000-000000000619", + }, + "timelineActivities": { + "id": "00000000-0000-0000-0000-000000000622", + }, + "title": { + "id": "00000000-0000-0000-0000-000000000613", + }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000610", + }, + "updatedBy": { + "id": "00000000-0000-0000-0000-000000000618", + }, }, - "id": "00000000-0000-0000-0000-000000000667", + "id": "00000000-0000-0000-0000-000000000668", "views": { "allTasks": { - "id": "00000000-0000-0000-0000-000000000631", + "id": "00000000-0000-0000-0000-000000000632", "viewFieldGroups": {}, "viewFields": { "assignee": { - "id": "00000000-0000-0000-0000-000000000628", - }, - "bodyV2": { "id": "00000000-0000-0000-0000-000000000629", }, - "createdAt": { + "bodyV2": { "id": "00000000-0000-0000-0000-000000000630", }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000626", + "createdAt": { + "id": "00000000-0000-0000-0000-000000000631", }, - "dueAt": { + "createdBy": { "id": "00000000-0000-0000-0000-000000000627", }, - "status": { - "id": "00000000-0000-0000-0000-000000000624", + "dueAt": { + "id": "00000000-0000-0000-0000-000000000628", }, - "taskTargets": { + "status": { "id": "00000000-0000-0000-0000-000000000625", }, + "taskTargets": { + "id": "00000000-0000-0000-0000-000000000626", + }, "title": { - "id": "00000000-0000-0000-0000-000000000623", + "id": "00000000-0000-0000-0000-000000000624", }, }, "viewGroups": {}, }, "assignedToMe": { - "id": "00000000-0000-0000-0000-000000000643", + "id": "00000000-0000-0000-0000-000000000644", "viewFieldGroups": {}, "viewFields": { "assignee": { - "id": "00000000-0000-0000-0000-000000000636", - }, - "bodyV2": { "id": "00000000-0000-0000-0000-000000000637", }, - "createdAt": { + "bodyV2": { "id": "00000000-0000-0000-0000-000000000638", }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000634", + "createdAt": { + "id": "00000000-0000-0000-0000-000000000639", }, - "dueAt": { + "createdBy": { "id": "00000000-0000-0000-0000-000000000635", }, + "dueAt": { + "id": "00000000-0000-0000-0000-000000000636", + }, "taskTargets": { - "id": "00000000-0000-0000-0000-000000000633", + "id": "00000000-0000-0000-0000-000000000634", }, "title": { - "id": "00000000-0000-0000-0000-000000000632", + "id": "00000000-0000-0000-0000-000000000633", }, }, "viewGroups": { "done": { - "id": "00000000-0000-0000-0000-000000000641", - }, - "empty": { "id": "00000000-0000-0000-0000-000000000642", }, + "empty": { + "id": "00000000-0000-0000-0000-000000000643", + }, "inProgress": { - "id": "00000000-0000-0000-0000-000000000640", + "id": "00000000-0000-0000-0000-000000000641", }, "todo": { - "id": "00000000-0000-0000-0000-000000000639", + "id": "00000000-0000-0000-0000-000000000640", }, }, }, "byStatus": { - "id": "00000000-0000-0000-0000-000000000652", + "id": "00000000-0000-0000-0000-000000000653", "viewFieldGroups": {}, "viewFields": { "assignee": { - "id": "00000000-0000-0000-0000-000000000647", - }, - "createdAt": { "id": "00000000-0000-0000-0000-000000000648", }, + "createdAt": { + "id": "00000000-0000-0000-0000-000000000649", + }, "dueAt": { - "id": "00000000-0000-0000-0000-000000000646", + "id": "00000000-0000-0000-0000-000000000647", }, "status": { - "id": "00000000-0000-0000-0000-000000000645", + "id": "00000000-0000-0000-0000-000000000646", }, "title": { - "id": "00000000-0000-0000-0000-000000000644", + "id": "00000000-0000-0000-0000-000000000645", }, }, "viewGroups": { "done": { - "id": "00000000-0000-0000-0000-000000000651", + "id": "00000000-0000-0000-0000-000000000652", }, "inProgress": { - "id": "00000000-0000-0000-0000-000000000650", + "id": "00000000-0000-0000-0000-000000000651", }, "todo": { - "id": "00000000-0000-0000-0000-000000000649", + "id": "00000000-0000-0000-0000-000000000650", }, }, }, "taskRecordPageFields": { - "id": "00000000-0000-0000-0000-000000000666", + "id": "00000000-0000-0000-0000-000000000667", "viewFieldGroups": { "general": { - "id": "00000000-0000-0000-0000-000000000664", + "id": "00000000-0000-0000-0000-000000000665", }, "system": { - "id": "00000000-0000-0000-0000-000000000665", + "id": "00000000-0000-0000-0000-000000000666", }, }, "viewFields": { "assignee": { - "id": "00000000-0000-0000-0000-000000000655", - }, - "attachments": { - "id": "00000000-0000-0000-0000-000000000662", - }, - "bodyV2": { - "id": "00000000-0000-0000-0000-000000000659", - }, - "createdAt": { "id": "00000000-0000-0000-0000-000000000656", }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000657", - }, - "dueAt": { - "id": "00000000-0000-0000-0000-000000000653", - }, - "status": { - "id": "00000000-0000-0000-0000-000000000654", - }, - "taskTargets": { - "id": "00000000-0000-0000-0000-000000000658", - }, - "timelineActivities": { + "attachments": { "id": "00000000-0000-0000-0000-000000000663", }, - "updatedAt": { + "bodyV2": { "id": "00000000-0000-0000-0000-000000000660", }, - "updatedBy": { + "createdAt": { + "id": "00000000-0000-0000-0000-000000000657", + }, + "createdBy": { + "id": "00000000-0000-0000-0000-000000000658", + }, + "dueAt": { + "id": "00000000-0000-0000-0000-000000000654", + }, + "status": { + "id": "00000000-0000-0000-0000-000000000655", + }, + "taskTargets": { + "id": "00000000-0000-0000-0000-000000000659", + }, + "timelineActivities": { + "id": "00000000-0000-0000-0000-000000000664", + }, + "updatedAt": { "id": "00000000-0000-0000-0000-000000000661", }, + "updatedBy": { + "id": "00000000-0000-0000-0000-000000000662", + }, }, "viewGroups": {}, }, @@ -2239,62 +2242,62 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "taskTarget": { "fields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000669", - }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000676", - }, - "deletedAt": { - "id": "00000000-0000-0000-0000-000000000671", - }, - "id": { - "id": "00000000-0000-0000-0000-000000000668", - }, - "position": { - "id": "00000000-0000-0000-0000-000000000678", - }, - "searchVector": { - "id": "00000000-0000-0000-0000-000000000679", - }, - "targetCompany": { - "id": "00000000-0000-0000-0000-000000000674", - }, - "targetOpportunity": { - "id": "00000000-0000-0000-0000-000000000675", - }, - "targetPerson": { - "id": "00000000-0000-0000-0000-000000000673", - }, - "task": { - "id": "00000000-0000-0000-0000-000000000672", - }, - "updatedAt": { "id": "00000000-0000-0000-0000-000000000670", }, - "updatedBy": { + "createdBy": { "id": "00000000-0000-0000-0000-000000000677", }, + "deletedAt": { + "id": "00000000-0000-0000-0000-000000000672", + }, + "id": { + "id": "00000000-0000-0000-0000-000000000669", + }, + "position": { + "id": "00000000-0000-0000-0000-000000000679", + }, + "searchVector": { + "id": "00000000-0000-0000-0000-000000000680", + }, + "targetCompany": { + "id": "00000000-0000-0000-0000-000000000675", + }, + "targetOpportunity": { + "id": "00000000-0000-0000-0000-000000000676", + }, + "targetPerson": { + "id": "00000000-0000-0000-0000-000000000674", + }, + "task": { + "id": "00000000-0000-0000-0000-000000000673", + }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000671", + }, + "updatedBy": { + "id": "00000000-0000-0000-0000-000000000678", + }, }, - "id": "00000000-0000-0000-0000-000000000686", + "id": "00000000-0000-0000-0000-000000000687", "views": { "allTaskTargets": { - "id": "00000000-0000-0000-0000-000000000685", + "id": "00000000-0000-0000-0000-000000000686", "viewFieldGroups": {}, "viewFields": { "id": { - "id": "00000000-0000-0000-0000-000000000680", + "id": "00000000-0000-0000-0000-000000000681", }, "targetCompany": { - "id": "00000000-0000-0000-0000-000000000683", - }, - "targetOpportunity": { "id": "00000000-0000-0000-0000-000000000684", }, + "targetOpportunity": { + "id": "00000000-0000-0000-0000-000000000685", + }, "targetPerson": { - "id": "00000000-0000-0000-0000-000000000682", + "id": "00000000-0000-0000-0000-000000000683", }, "task": { - "id": "00000000-0000-0000-0000-000000000681", + "id": "00000000-0000-0000-0000-000000000682", }, }, "viewGroups": {}, @@ -2304,131 +2307,131 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "timelineActivity": { "fields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000688", - }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000709", - }, - "deletedAt": { - "id": "00000000-0000-0000-0000-000000000690", - }, - "happensAt": { - "id": "00000000-0000-0000-0000-000000000691", - }, - "id": { - "id": "00000000-0000-0000-0000-000000000687", - }, - "linkedObjectMetadataId": { - "id": "00000000-0000-0000-0000-000000000708", - }, - "linkedRecordCachedName": { - "id": "00000000-0000-0000-0000-000000000706", - }, - "linkedRecordId": { - "id": "00000000-0000-0000-0000-000000000707", - }, - "name": { - "id": "00000000-0000-0000-0000-000000000692", - }, - "position": { - "id": "00000000-0000-0000-0000-000000000711", - }, - "properties": { - "id": "00000000-0000-0000-0000-000000000693", - }, - "searchVector": { - "id": "00000000-0000-0000-0000-000000000712", - }, - "targetCompany": { - "id": "00000000-0000-0000-0000-000000000696", - }, - "targetDashboard": { - "id": "00000000-0000-0000-0000-000000000703", - }, - "targetMessageCampaign": { - "id": "00000000-0000-0000-0000-000000000705", - }, - "targetMessageList": { - "id": "00000000-0000-0000-0000-000000000704", - }, - "targetNote": { - "id": "00000000-0000-0000-0000-000000000699", - }, - "targetOpportunity": { - "id": "00000000-0000-0000-0000-000000000697", - }, - "targetPerson": { - "id": "00000000-0000-0000-0000-000000000695", - }, - "targetTask": { - "id": "00000000-0000-0000-0000-000000000698", - }, - "targetWorkflow": { - "id": "00000000-0000-0000-0000-000000000700", - }, - "targetWorkflowRun": { - "id": "00000000-0000-0000-0000-000000000702", - }, - "targetWorkflowVersion": { - "id": "00000000-0000-0000-0000-000000000701", - }, - "updatedAt": { "id": "00000000-0000-0000-0000-000000000689", }, - "updatedBy": { + "createdBy": { "id": "00000000-0000-0000-0000-000000000710", }, - "workspaceMember": { + "deletedAt": { + "id": "00000000-0000-0000-0000-000000000691", + }, + "happensAt": { + "id": "00000000-0000-0000-0000-000000000692", + }, + "id": { + "id": "00000000-0000-0000-0000-000000000688", + }, + "linkedObjectMetadataId": { + "id": "00000000-0000-0000-0000-000000000709", + }, + "linkedRecordCachedName": { + "id": "00000000-0000-0000-0000-000000000707", + }, + "linkedRecordId": { + "id": "00000000-0000-0000-0000-000000000708", + }, + "name": { + "id": "00000000-0000-0000-0000-000000000693", + }, + "position": { + "id": "00000000-0000-0000-0000-000000000712", + }, + "properties": { "id": "00000000-0000-0000-0000-000000000694", }, + "searchVector": { + "id": "00000000-0000-0000-0000-000000000713", + }, + "targetCompany": { + "id": "00000000-0000-0000-0000-000000000697", + }, + "targetDashboard": { + "id": "00000000-0000-0000-0000-000000000704", + }, + "targetMessageCampaign": { + "id": "00000000-0000-0000-0000-000000000706", + }, + "targetMessageList": { + "id": "00000000-0000-0000-0000-000000000705", + }, + "targetNote": { + "id": "00000000-0000-0000-0000-000000000700", + }, + "targetOpportunity": { + "id": "00000000-0000-0000-0000-000000000698", + }, + "targetPerson": { + "id": "00000000-0000-0000-0000-000000000696", + }, + "targetTask": { + "id": "00000000-0000-0000-0000-000000000699", + }, + "targetWorkflow": { + "id": "00000000-0000-0000-0000-000000000701", + }, + "targetWorkflowRun": { + "id": "00000000-0000-0000-0000-000000000703", + }, + "targetWorkflowVersion": { + "id": "00000000-0000-0000-0000-000000000702", + }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000690", + }, + "updatedBy": { + "id": "00000000-0000-0000-0000-000000000711", + }, + "workspaceMember": { + "id": "00000000-0000-0000-0000-000000000695", + }, }, - "id": "00000000-0000-0000-0000-000000000728", + "id": "00000000-0000-0000-0000-000000000729", "views": { "allTimelineActivities": { - "id": "00000000-0000-0000-0000-000000000727", + "id": "00000000-0000-0000-0000-000000000728", "viewFieldGroups": {}, "viewFields": { "happensAt": { - "id": "00000000-0000-0000-0000-000000000714", - }, - "linkedRecordCachedName": { - "id": "00000000-0000-0000-0000-000000000717", - }, - "name": { - "id": "00000000-0000-0000-0000-000000000713", - }, - "properties": { "id": "00000000-0000-0000-0000-000000000715", }, - "targetCompany": { - "id": "00000000-0000-0000-0000-000000000719", - }, - "targetDashboard": { - "id": "00000000-0000-0000-0000-000000000726", - }, - "targetNote": { - "id": "00000000-0000-0000-0000-000000000722", - }, - "targetOpportunity": { - "id": "00000000-0000-0000-0000-000000000720", - }, - "targetPerson": { + "linkedRecordCachedName": { "id": "00000000-0000-0000-0000-000000000718", }, - "targetTask": { - "id": "00000000-0000-0000-0000-000000000721", + "name": { + "id": "00000000-0000-0000-0000-000000000714", }, - "targetWorkflow": { + "properties": { + "id": "00000000-0000-0000-0000-000000000716", + }, + "targetCompany": { + "id": "00000000-0000-0000-0000-000000000720", + }, + "targetDashboard": { + "id": "00000000-0000-0000-0000-000000000727", + }, + "targetNote": { "id": "00000000-0000-0000-0000-000000000723", }, - "targetWorkflowRun": { - "id": "00000000-0000-0000-0000-000000000725", + "targetOpportunity": { + "id": "00000000-0000-0000-0000-000000000721", }, - "targetWorkflowVersion": { + "targetPerson": { + "id": "00000000-0000-0000-0000-000000000719", + }, + "targetTask": { + "id": "00000000-0000-0000-0000-000000000722", + }, + "targetWorkflow": { "id": "00000000-0000-0000-0000-000000000724", }, + "targetWorkflowRun": { + "id": "00000000-0000-0000-0000-000000000726", + }, + "targetWorkflowVersion": { + "id": "00000000-0000-0000-0000-000000000725", + }, "workspaceMember": { - "id": "00000000-0000-0000-0000-000000000716", + "id": "00000000-0000-0000-0000-000000000717", }, }, "viewGroups": {}, @@ -2438,77 +2441,77 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "workflow": { "fields": { "attachments": { - "id": "00000000-0000-0000-0000-000000000741", - }, - "automatedTriggers": { - "id": "00000000-0000-0000-0000-000000000739", - }, - "createdAt": { - "id": "00000000-0000-0000-0000-000000000730", - }, - "createdBy": { "id": "00000000-0000-0000-0000-000000000742", }, - "deletedAt": { - "id": "00000000-0000-0000-0000-000000000732", - }, - "id": { - "id": "00000000-0000-0000-0000-000000000729", - }, - "lastPublishedVersionId": { - "id": "00000000-0000-0000-0000-000000000734", - }, - "name": { - "id": "00000000-0000-0000-0000-000000000733", - }, - "position": { - "id": "00000000-0000-0000-0000-000000000736", - }, - "runs": { - "id": "00000000-0000-0000-0000-000000000738", - }, - "searchVector": { - "id": "00000000-0000-0000-0000-000000000744", - }, - "statuses": { - "id": "00000000-0000-0000-0000-000000000735", - }, - "timelineActivities": { + "automatedTriggers": { "id": "00000000-0000-0000-0000-000000000740", }, - "updatedAt": { + "createdAt": { "id": "00000000-0000-0000-0000-000000000731", }, - "updatedBy": { + "createdBy": { "id": "00000000-0000-0000-0000-000000000743", }, - "versions": { + "deletedAt": { + "id": "00000000-0000-0000-0000-000000000733", + }, + "id": { + "id": "00000000-0000-0000-0000-000000000730", + }, + "lastPublishedVersionId": { + "id": "00000000-0000-0000-0000-000000000735", + }, + "name": { + "id": "00000000-0000-0000-0000-000000000734", + }, + "position": { "id": "00000000-0000-0000-0000-000000000737", }, + "runs": { + "id": "00000000-0000-0000-0000-000000000739", + }, + "searchVector": { + "id": "00000000-0000-0000-0000-000000000745", + }, + "statuses": { + "id": "00000000-0000-0000-0000-000000000736", + }, + "timelineActivities": { + "id": "00000000-0000-0000-0000-000000000741", + }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000732", + }, + "updatedBy": { + "id": "00000000-0000-0000-0000-000000000744", + }, + "versions": { + "id": "00000000-0000-0000-0000-000000000738", + }, }, - "id": "00000000-0000-0000-0000-000000000752", + "id": "00000000-0000-0000-0000-000000000753", "views": { "allWorkflows": { - "id": "00000000-0000-0000-0000-000000000751", + "id": "00000000-0000-0000-0000-000000000752", "viewFieldGroups": {}, "viewFields": { "createdBy": { - "id": "00000000-0000-0000-0000-000000000748", + "id": "00000000-0000-0000-0000-000000000749", }, "name": { - "id": "00000000-0000-0000-0000-000000000745", - }, - "runs": { - "id": "00000000-0000-0000-0000-000000000750", - }, - "statuses": { "id": "00000000-0000-0000-0000-000000000746", }, - "updatedAt": { + "runs": { + "id": "00000000-0000-0000-0000-000000000751", + }, + "statuses": { "id": "00000000-0000-0000-0000-000000000747", }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000748", + }, "versions": { - "id": "00000000-0000-0000-0000-000000000749", + "id": "00000000-0000-0000-0000-000000000750", }, }, "viewGroups": {}, @@ -2518,79 +2521,79 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "workflowAutomatedTrigger": { "fields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000754", - }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000760", - }, - "deletedAt": { - "id": "00000000-0000-0000-0000-000000000756", - }, - "id": { - "id": "00000000-0000-0000-0000-000000000753", - }, - "position": { - "id": "00000000-0000-0000-0000-000000000762", - }, - "searchVector": { - "id": "00000000-0000-0000-0000-000000000763", - }, - "settings": { - "id": "00000000-0000-0000-0000-000000000758", - }, - "type": { - "id": "00000000-0000-0000-0000-000000000757", - }, - "updatedAt": { "id": "00000000-0000-0000-0000-000000000755", }, - "updatedBy": { + "createdBy": { "id": "00000000-0000-0000-0000-000000000761", }, - "workflow": { + "deletedAt": { + "id": "00000000-0000-0000-0000-000000000757", + }, + "id": { + "id": "00000000-0000-0000-0000-000000000754", + }, + "position": { + "id": "00000000-0000-0000-0000-000000000763", + }, + "searchVector": { + "id": "00000000-0000-0000-0000-000000000764", + }, + "settings": { "id": "00000000-0000-0000-0000-000000000759", }, + "type": { + "id": "00000000-0000-0000-0000-000000000758", + }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000756", + }, + "updatedBy": { + "id": "00000000-0000-0000-0000-000000000762", + }, + "workflow": { + "id": "00000000-0000-0000-0000-000000000760", + }, }, - "id": "00000000-0000-0000-0000-000000000775", + "id": "00000000-0000-0000-0000-000000000776", "views": { "allWorkflowAutomatedTriggers": { - "id": "00000000-0000-0000-0000-000000000767", + "id": "00000000-0000-0000-0000-000000000768", "viewFieldGroups": {}, "viewFields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000766", + "id": "00000000-0000-0000-0000-000000000767", }, "type": { - "id": "00000000-0000-0000-0000-000000000764", + "id": "00000000-0000-0000-0000-000000000765", }, "workflow": { - "id": "00000000-0000-0000-0000-000000000765", + "id": "00000000-0000-0000-0000-000000000766", }, }, "viewGroups": {}, }, "workflowAutomatedTriggerRecordPageFields": { - "id": "00000000-0000-0000-0000-000000000774", + "id": "00000000-0000-0000-0000-000000000775", "viewFieldGroups": { "general": { - "id": "00000000-0000-0000-0000-000000000772", + "id": "00000000-0000-0000-0000-000000000773", }, "system": { - "id": "00000000-0000-0000-0000-000000000773", + "id": "00000000-0000-0000-0000-000000000774", }, }, "viewFields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000770", - }, - "createdBy": { "id": "00000000-0000-0000-0000-000000000771", }, + "createdBy": { + "id": "00000000-0000-0000-0000-000000000772", + }, "type": { - "id": "00000000-0000-0000-0000-000000000768", + "id": "00000000-0000-0000-0000-000000000769", }, "workflow": { - "id": "00000000-0000-0000-0000-000000000769", + "id": "00000000-0000-0000-0000-000000000770", }, }, "viewGroups": {}, @@ -2600,134 +2603,134 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "workflowRun": { "fields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000777", - }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000788", - }, - "deletedAt": { - "id": "00000000-0000-0000-0000-000000000779", - }, - "endedAt": { - "id": "00000000-0000-0000-0000-000000000785", - }, - "enqueuedAt": { - "id": "00000000-0000-0000-0000-000000000783", - }, - "id": { - "id": "00000000-0000-0000-0000-000000000776", - }, - "name": { - "id": "00000000-0000-0000-0000-000000000780", - }, - "position": { - "id": "00000000-0000-0000-0000-000000000787", - }, - "searchVector": { - "id": "00000000-0000-0000-0000-000000000793", - }, - "startedAt": { - "id": "00000000-0000-0000-0000-000000000784", - }, - "state": { - "id": "00000000-0000-0000-0000-000000000790", - }, - "status": { - "id": "00000000-0000-0000-0000-000000000786", - }, - "stepLogs": { - "id": "00000000-0000-0000-0000-000000000791", - }, - "timelineActivities": { - "id": "00000000-0000-0000-0000-000000000792", - }, - "updatedAt": { "id": "00000000-0000-0000-0000-000000000778", }, - "updatedBy": { + "createdBy": { "id": "00000000-0000-0000-0000-000000000789", }, - "workflow": { - "id": "00000000-0000-0000-0000-000000000782", + "deletedAt": { + "id": "00000000-0000-0000-0000-000000000780", }, - "workflowVersion": { + "endedAt": { + "id": "00000000-0000-0000-0000-000000000786", + }, + "enqueuedAt": { + "id": "00000000-0000-0000-0000-000000000784", + }, + "id": { + "id": "00000000-0000-0000-0000-000000000777", + }, + "name": { "id": "00000000-0000-0000-0000-000000000781", }, + "position": { + "id": "00000000-0000-0000-0000-000000000788", + }, + "searchVector": { + "id": "00000000-0000-0000-0000-000000000794", + }, + "startedAt": { + "id": "00000000-0000-0000-0000-000000000785", + }, + "state": { + "id": "00000000-0000-0000-0000-000000000791", + }, + "status": { + "id": "00000000-0000-0000-0000-000000000787", + }, + "stepLogs": { + "id": "00000000-0000-0000-0000-000000000792", + }, + "timelineActivities": { + "id": "00000000-0000-0000-0000-000000000793", + }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000779", + }, + "updatedBy": { + "id": "00000000-0000-0000-0000-000000000790", + }, + "workflow": { + "id": "00000000-0000-0000-0000-000000000783", + }, + "workflowVersion": { + "id": "00000000-0000-0000-0000-000000000782", + }, }, - "id": "00000000-0000-0000-0000-000000000816", + "id": "00000000-0000-0000-0000-000000000817", "views": { "allWorkflowRuns": { - "id": "00000000-0000-0000-0000-000000000800", + "id": "00000000-0000-0000-0000-000000000801", "viewFieldGroups": {}, "viewFields": { "createdBy": { - "id": "00000000-0000-0000-0000-000000000798", + "id": "00000000-0000-0000-0000-000000000799", }, "name": { - "id": "00000000-0000-0000-0000-000000000794", - }, - "startedAt": { - "id": "00000000-0000-0000-0000-000000000797", - }, - "status": { - "id": "00000000-0000-0000-0000-000000000796", - }, - "workflow": { "id": "00000000-0000-0000-0000-000000000795", }, + "startedAt": { + "id": "00000000-0000-0000-0000-000000000798", + }, + "status": { + "id": "00000000-0000-0000-0000-000000000797", + }, + "workflow": { + "id": "00000000-0000-0000-0000-000000000796", + }, "workflowVersion": { - "id": "00000000-0000-0000-0000-000000000799", + "id": "00000000-0000-0000-0000-000000000800", }, }, "viewGroups": {}, }, "workflowRunRecordPageFields": { - "id": "00000000-0000-0000-0000-000000000815", + "id": "00000000-0000-0000-0000-000000000816", "viewFieldGroups": { "general": { - "id": "00000000-0000-0000-0000-000000000813", + "id": "00000000-0000-0000-0000-000000000814", }, "system": { - "id": "00000000-0000-0000-0000-000000000814", + "id": "00000000-0000-0000-0000-000000000815", }, }, "viewFields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000806", - }, - "createdBy": { "id": "00000000-0000-0000-0000-000000000807", }, - "endedAt": { - "id": "00000000-0000-0000-0000-000000000805", - }, - "enqueuedAt": { + "createdBy": { "id": "00000000-0000-0000-0000-000000000808", }, - "startedAt": { - "id": "00000000-0000-0000-0000-000000000804", + "endedAt": { + "id": "00000000-0000-0000-0000-000000000806", }, - "state": { + "enqueuedAt": { "id": "00000000-0000-0000-0000-000000000809", }, - "status": { - "id": "00000000-0000-0000-0000-000000000801", + "startedAt": { + "id": "00000000-0000-0000-0000-000000000805", }, - "timelineActivities": { - "id": "00000000-0000-0000-0000-000000000812", - }, - "updatedAt": { + "state": { "id": "00000000-0000-0000-0000-000000000810", }, - "updatedBy": { - "id": "00000000-0000-0000-0000-000000000811", - }, - "workflow": { + "status": { "id": "00000000-0000-0000-0000-000000000802", }, - "workflowVersion": { + "timelineActivities": { + "id": "00000000-0000-0000-0000-000000000813", + }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000811", + }, + "updatedBy": { + "id": "00000000-0000-0000-0000-000000000812", + }, + "workflow": { "id": "00000000-0000-0000-0000-000000000803", }, + "workflowVersion": { + "id": "00000000-0000-0000-0000-000000000804", + }, }, "viewGroups": {}, }, @@ -2736,115 +2739,115 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "workflowVersion": { "fields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000818", - }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000830", - }, - "deletedAt": { - "id": "00000000-0000-0000-0000-000000000820", - }, - "id": { - "id": "00000000-0000-0000-0000-000000000817", - }, - "name": { - "id": "00000000-0000-0000-0000-000000000821", - }, - "position": { - "id": "00000000-0000-0000-0000-000000000825", - }, - "runs": { - "id": "00000000-0000-0000-0000-000000000826", - }, - "searchVector": { - "id": "00000000-0000-0000-0000-000000000829", - }, - "status": { - "id": "00000000-0000-0000-0000-000000000824", - }, - "steps": { - "id": "00000000-0000-0000-0000-000000000827", - }, - "timelineActivities": { - "id": "00000000-0000-0000-0000-000000000828", - }, - "trigger": { - "id": "00000000-0000-0000-0000-000000000823", - }, - "updatedAt": { "id": "00000000-0000-0000-0000-000000000819", }, - "updatedBy": { + "createdBy": { "id": "00000000-0000-0000-0000-000000000831", }, - "workflow": { + "deletedAt": { + "id": "00000000-0000-0000-0000-000000000821", + }, + "id": { + "id": "00000000-0000-0000-0000-000000000818", + }, + "name": { "id": "00000000-0000-0000-0000-000000000822", }, + "position": { + "id": "00000000-0000-0000-0000-000000000826", + }, + "runs": { + "id": "00000000-0000-0000-0000-000000000827", + }, + "searchVector": { + "id": "00000000-0000-0000-0000-000000000830", + }, + "status": { + "id": "00000000-0000-0000-0000-000000000825", + }, + "steps": { + "id": "00000000-0000-0000-0000-000000000828", + }, + "timelineActivities": { + "id": "00000000-0000-0000-0000-000000000829", + }, + "trigger": { + "id": "00000000-0000-0000-0000-000000000824", + }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000820", + }, + "updatedBy": { + "id": "00000000-0000-0000-0000-000000000832", + }, + "workflow": { + "id": "00000000-0000-0000-0000-000000000823", + }, }, - "id": "00000000-0000-0000-0000-000000000851", + "id": "00000000-0000-0000-0000-000000000852", "views": { "allWorkflowVersions": { - "id": "00000000-0000-0000-0000-000000000837", + "id": "00000000-0000-0000-0000-000000000838", "viewFieldGroups": {}, "viewFields": { "name": { - "id": "00000000-0000-0000-0000-000000000832", + "id": "00000000-0000-0000-0000-000000000833", }, "runs": { - "id": "00000000-0000-0000-0000-000000000836", + "id": "00000000-0000-0000-0000-000000000837", }, "status": { - "id": "00000000-0000-0000-0000-000000000834", - }, - "updatedAt": { "id": "00000000-0000-0000-0000-000000000835", }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000836", + }, "workflow": { - "id": "00000000-0000-0000-0000-000000000833", + "id": "00000000-0000-0000-0000-000000000834", }, }, "viewGroups": {}, }, "workflowVersionRecordPageFields": { - "id": "00000000-0000-0000-0000-000000000850", + "id": "00000000-0000-0000-0000-000000000851", "viewFieldGroups": { "general": { - "id": "00000000-0000-0000-0000-000000000848", + "id": "00000000-0000-0000-0000-000000000849", }, "system": { - "id": "00000000-0000-0000-0000-000000000849", + "id": "00000000-0000-0000-0000-000000000850", }, }, "viewFields": { "createdAt": { - "id": "00000000-0000-0000-0000-000000000841", - }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000843", - }, - "runs": { - "id": "00000000-0000-0000-0000-000000000846", - }, - "status": { - "id": "00000000-0000-0000-0000-000000000838", - }, - "steps": { "id": "00000000-0000-0000-0000-000000000842", }, - "timelineActivities": { - "id": "00000000-0000-0000-0000-000000000847", - }, - "trigger": { - "id": "00000000-0000-0000-0000-000000000840", - }, - "updatedAt": { + "createdBy": { "id": "00000000-0000-0000-0000-000000000844", }, - "updatedBy": { + "runs": { + "id": "00000000-0000-0000-0000-000000000847", + }, + "status": { + "id": "00000000-0000-0000-0000-000000000839", + }, + "steps": { + "id": "00000000-0000-0000-0000-000000000843", + }, + "timelineActivities": { + "id": "00000000-0000-0000-0000-000000000848", + }, + "trigger": { + "id": "00000000-0000-0000-0000-000000000841", + }, + "updatedAt": { "id": "00000000-0000-0000-0000-000000000845", }, + "updatedBy": { + "id": "00000000-0000-0000-0000-000000000846", + }, "workflow": { - "id": "00000000-0000-0000-0000-000000000839", + "id": "00000000-0000-0000-0000-000000000840", }, }, "viewGroups": {}, @@ -2854,126 +2857,126 @@ exports[`getStandardObjectMetadataRelatedEntityIds should return standard object "workspaceMember": { "fields": { "accountOwnerForCompanies": { - "id": "00000000-0000-0000-0000-000000000866", - }, - "assignedTasks": { - "id": "00000000-0000-0000-0000-000000000864", - }, - "avatarUrl": { - "id": "00000000-0000-0000-0000-000000000860", - }, - "blocklist": { - "id": "00000000-0000-0000-0000-000000000868", - }, - "calendarEventParticipants": { - "id": "00000000-0000-0000-0000-000000000869", - }, - "calendarStartDay": { - "id": "00000000-0000-0000-0000-000000000875", - }, - "colorScheme": { - "id": "00000000-0000-0000-0000-000000000858", - }, - "createdAt": { - "id": "00000000-0000-0000-0000-000000000853", - }, - "createdBy": { - "id": "00000000-0000-0000-0000-000000000877", - }, - "dateFormat": { - "id": "00000000-0000-0000-0000-000000000872", - }, - "deletedAt": { - "id": "00000000-0000-0000-0000-000000000855", - }, - "id": { - "id": "00000000-0000-0000-0000-000000000852", - }, - "jobTitle": { - "id": "00000000-0000-0000-0000-000000000862", - }, - "locale": { - "id": "00000000-0000-0000-0000-000000000859", - }, - "messageParticipants": { "id": "00000000-0000-0000-0000-000000000867", }, - "name": { - "id": "00000000-0000-0000-0000-000000000857", - }, - "numberFormat": { - "id": "00000000-0000-0000-0000-000000000876", - }, - "ownedOpportunities": { + "assignedTasks": { "id": "00000000-0000-0000-0000-000000000865", }, - "position": { - "id": "00000000-0000-0000-0000-000000000856", - }, - "searchVector": { - "id": "00000000-0000-0000-0000-000000000874", - }, - "timeFormat": { - "id": "00000000-0000-0000-0000-000000000873", - }, - "timeZone": { - "id": "00000000-0000-0000-0000-000000000871", - }, - "timelineActivities": { - "id": "00000000-0000-0000-0000-000000000870", - }, - "updatedAt": { - "id": "00000000-0000-0000-0000-000000000854", - }, - "updatedBy": { - "id": "00000000-0000-0000-0000-000000000878", - }, - "userEmail": { + "avatarUrl": { "id": "00000000-0000-0000-0000-000000000861", }, - "userId": { + "blocklist": { + "id": "00000000-0000-0000-0000-000000000869", + }, + "calendarEventParticipants": { + "id": "00000000-0000-0000-0000-000000000870", + }, + "calendarStartDay": { + "id": "00000000-0000-0000-0000-000000000876", + }, + "colorScheme": { + "id": "00000000-0000-0000-0000-000000000859", + }, + "createdAt": { + "id": "00000000-0000-0000-0000-000000000854", + }, + "createdBy": { + "id": "00000000-0000-0000-0000-000000000878", + }, + "dateFormat": { + "id": "00000000-0000-0000-0000-000000000873", + }, + "deletedAt": { + "id": "00000000-0000-0000-0000-000000000856", + }, + "id": { + "id": "00000000-0000-0000-0000-000000000853", + }, + "jobTitle": { "id": "00000000-0000-0000-0000-000000000863", }, + "locale": { + "id": "00000000-0000-0000-0000-000000000860", + }, + "messageParticipants": { + "id": "00000000-0000-0000-0000-000000000868", + }, + "name": { + "id": "00000000-0000-0000-0000-000000000858", + }, + "numberFormat": { + "id": "00000000-0000-0000-0000-000000000877", + }, + "ownedOpportunities": { + "id": "00000000-0000-0000-0000-000000000866", + }, + "position": { + "id": "00000000-0000-0000-0000-000000000857", + }, + "searchVector": { + "id": "00000000-0000-0000-0000-000000000875", + }, + "timeFormat": { + "id": "00000000-0000-0000-0000-000000000874", + }, + "timeZone": { + "id": "00000000-0000-0000-0000-000000000872", + }, + "timelineActivities": { + "id": "00000000-0000-0000-0000-000000000871", + }, + "updatedAt": { + "id": "00000000-0000-0000-0000-000000000855", + }, + "updatedBy": { + "id": "00000000-0000-0000-0000-000000000879", + }, + "userEmail": { + "id": "00000000-0000-0000-0000-000000000862", + }, + "userId": { + "id": "00000000-0000-0000-0000-000000000864", + }, }, - "id": "00000000-0000-0000-0000-000000000891", + "id": "00000000-0000-0000-0000-000000000892", "views": { "allWorkspaceMembers": { - "id": "00000000-0000-0000-0000-000000000890", + "id": "00000000-0000-0000-0000-000000000891", "viewFieldGroups": {}, "viewFields": { "assignedTasks": { - "id": "00000000-0000-0000-0000-000000000889", + "id": "00000000-0000-0000-0000-000000000890", }, "avatarUrl": { - "id": "00000000-0000-0000-0000-000000000881", - }, - "colorScheme": { "id": "00000000-0000-0000-0000-000000000882", }, - "createdAt": { - "id": "00000000-0000-0000-0000-000000000887", - }, - "dateFormat": { - "id": "00000000-0000-0000-0000-000000000885", - }, - "locale": { + "colorScheme": { "id": "00000000-0000-0000-0000-000000000883", }, - "name": { - "id": "00000000-0000-0000-0000-000000000879", - }, - "ownedOpportunities": { + "createdAt": { "id": "00000000-0000-0000-0000-000000000888", }, - "timeFormat": { + "dateFormat": { "id": "00000000-0000-0000-0000-000000000886", }, - "timeZone": { + "locale": { "id": "00000000-0000-0000-0000-000000000884", }, - "userEmail": { + "name": { "id": "00000000-0000-0000-0000-000000000880", }, + "ownedOpportunities": { + "id": "00000000-0000-0000-0000-000000000889", + }, + "timeFormat": { + "id": "00000000-0000-0000-0000-000000000887", + }, + "timeZone": { + "id": "00000000-0000-0000-0000-000000000885", + }, + "userEmail": { + "id": "00000000-0000-0000-0000-000000000881", + }, }, "viewGroups": {}, }, diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-message-standard-flat-field-metadata.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-message-standard-flat-field-metadata.util.ts index 4e4c41fa64..87894c5584 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-message-standard-flat-field-metadata.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-message-standard-flat-field-metadata.util.ts @@ -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, + }), }); diff --git a/packages/twenty-server/src/modules/messaging/common/query-hooks/message/apply-messages-visibility-restrictions.service.spec.ts b/packages/twenty-server/src/modules/messaging/common/query-hooks/message/apply-messages-visibility-restrictions.service.spec.ts index daf69615c7..dc5358f7a4 100644 --- a/packages/twenty-server/src/modules/messaging/common/query-hooks/message/apply-messages-visibility-restrictions.service.spec.ts +++ b/packages/twenty-server/src/modules/messaging/common/query-hooks/message/apply-messages-visibility-restrictions.service.spec.ts @@ -29,6 +29,7 @@ const createMockMessage = ( messageCampaign: null, messageCampaignId: null, deliveryStatus: null, + isDraft: false, deletedAt: null, createdAt: '2024-03-20T09:00:00Z', updatedAt: '2024-03-20T09:00:00Z', diff --git a/packages/twenty-server/src/modules/messaging/common/standard-objects/message.workspace-entity.ts b/packages/twenty-server/src/modules/messaging/common/standard-objects/message.workspace-entity.ts index 9a3f9f830a..ba57f0ab22 100644 --- a/packages/twenty-server/src/modules/messaging/common/standard-objects/message.workspace-entity.ts +++ b/packages/twenty-server/src/modules/messaging/common/standard-objects/message.workspace-entity.ts @@ -19,4 +19,5 @@ export class MessageWorkspaceEntity extends BaseWorkspaceEntity { messageCampaign: EntityRelation | null; messageCampaignId: string | null; deliveryStatus: string | null; + isDraft: boolean; } diff --git a/packages/twenty-server/src/modules/messaging/message-folder-manager/utils/MESSAGING_FOLDER_MANAGER_ALWAYS_EXCLUDED_FOLDERS.ts b/packages/twenty-server/src/modules/messaging/message-folder-manager/utils/MESSAGING_FOLDER_MANAGER_ALWAYS_EXCLUDED_FOLDERS.ts index 3e70413fd1..b753f73670 100644 --- a/packages/twenty-server/src/modules/messaging/message-folder-manager/utils/MESSAGING_FOLDER_MANAGER_ALWAYS_EXCLUDED_FOLDERS.ts +++ b/packages/twenty-server/src/modules/messaging/message-folder-manager/utils/MESSAGING_FOLDER_MANAGER_ALWAYS_EXCLUDED_FOLDERS.ts @@ -1,7 +1,6 @@ import { StandardFolder } from 'src/modules/messaging/message-import-manager/drivers/types/standard-folder'; export const MESSAGING_FOLDER_MANAGER_ALWAYS_EXCLUDED_FOLDERS = [ - StandardFolder.DRAFTS, StandardFolder.TRASH, StandardFolder.JUNK, ]; diff --git a/packages/twenty-server/src/modules/messaging/message-folder-manager/utils/__tests__/should-create-folder-by-default.util.spec.ts b/packages/twenty-server/src/modules/messaging/message-folder-manager/utils/__tests__/should-create-folder-by-default.util.spec.ts index b10370f793..52a01941e1 100644 --- a/packages/twenty-server/src/modules/messaging/message-folder-manager/utils/__tests__/should-create-folder-by-default.util.spec.ts +++ b/packages/twenty-server/src/modules/messaging/message-folder-manager/utils/__tests__/should-create-folder-by-default.util.spec.ts @@ -5,6 +5,7 @@ describe('shouldCreateFolderByDefault', () => { it('should allow creating user folders', () => { expect(shouldCreateFolderByDefault(StandardFolder.INBOX)).toBe(true); expect(shouldCreateFolderByDefault(StandardFolder.SENT)).toBe(true); + expect(shouldCreateFolderByDefault(StandardFolder.DRAFTS)).toBe(true); }); it('should allow creating custom folders', () => { @@ -13,7 +14,6 @@ describe('shouldCreateFolderByDefault', () => { }); it('should prevent creating system-excluded folders', () => { - expect(shouldCreateFolderByDefault(StandardFolder.DRAFTS)).toBe(false); expect(shouldCreateFolderByDefault(StandardFolder.TRASH)).toBe(false); expect(shouldCreateFolderByDefault(StandardFolder.JUNK)).toBe(false); }); diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/constants/messaging-gmail-excluded-system-labels.constant.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/constants/messaging-gmail-excluded-system-labels.constant.ts index 09e24112a4..719910c1c4 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/constants/messaging-gmail-excluded-system-labels.constant.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/constants/messaging-gmail-excluded-system-labels.constant.ts @@ -1,6 +1 @@ -export const MESSAGING_GMAIL_EXCLUDED_SYSTEM_LABELS = [ - 'TRASH', - 'SPAM', - 'DRAFT', - 'CHAT', -]; +export const MESSAGING_GMAIL_EXCLUDED_SYSTEM_LABELS = ['TRASH', 'SPAM', 'CHAT']; diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/__tests__/compute-gmail-exclude-search-filter.spec.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/__tests__/compute-gmail-exclude-search-filter.spec.ts index a9ab3768a0..68e73ad192 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/__tests__/compute-gmail-exclude-search-filter.spec.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/__tests__/compute-gmail-exclude-search-filter.spec.ts @@ -190,8 +190,8 @@ describe('computeGmailExcludeSearchFilter', () => { expect(result).toContain('-label:trash'); expect(result).toContain('-label:spam'); - expect(result).toContain('-label:draft'); expect(result).toContain('-label:chat'); + expect(result).not.toContain('-label:draft'); }); it('uses -category: syntax for category exclusions in ALL_FOLDERS mode', () => { diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/__tests__/parse-and-format-gmail-message.util.spec.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/__tests__/parse-and-format-gmail-message.util.spec.ts index f269acd972..94f8d765cf 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/__tests__/parse-and-format-gmail-message.util.spec.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/__tests__/parse-and-format-gmail-message.util.spec.ts @@ -152,4 +152,32 @@ describe('parseAndFormatGmailMessage', () => { expect(result).toBeNull(); }); + + it('should keep a draft missing a Message-ID header by synthesizing a fallback id', () => { + const result = parseAndFormatGmailMessage( + buildMessage( + [ + { name: 'From', value: 'me@example.com' }, + { name: 'To', value: 'alice@example.com' }, + ], + { labelIds: ['DRAFT'] }, + ), + connectedAccount, + ); + + expect(result?.isDraft).toBe(true); + expect(result?.headerMessageId).toBe('draft-msg-1'); + }); + + it('should still drop a non-draft message missing a Message-ID header', () => { + const result = parseAndFormatGmailMessage( + buildMessage([ + { name: 'From', value: 'sender@example.com' }, + { name: 'To', value: 'alice@example.com' }, + ]), + connectedAccount, + ); + + expect(result).toBeNull(); + }); }); diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-and-format-gmail-message.util.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-and-format-gmail-message.util.ts index a7f30951f2..ff45f68336 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-and-format-gmail-message.util.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-and-format-gmail-message.util.ts @@ -33,7 +33,18 @@ export const parseAndFormatGmailMessage = ( labelIds, } = parseGmailMessage(message); - if (!isDefined(from) || !isDefined(headerMessageId) || !isDefined(threadId)) { + const isDraft = (labelIds ?? []).includes('DRAFT'); + + // Gmail may omit the Message-ID header on drafts; synthesize a stable id from + // the message id so drafts aren't dropped. + const resolvedHeaderMessageId = + headerMessageId ?? (isDraft ? `draft-${id}` : undefined); + + if ( + !isDefined(from) || + !isDefined(resolvedHeaderMessageId) || + !isDefined(threadId) + ) { return null; } @@ -58,13 +69,13 @@ export const parseAndFormatGmailMessage = ( (participant) => participant.role !== MessageParticipantRole.FROM, ); - if (!hasRecipientParticipant) { + if (!hasRecipientParticipant && !isDraft) { return null; } return { externalId: id, - headerMessageId, + headerMessageId: resolvedHeaderMessageId, subject: subject || '', messageThreadExternalId: threadId, receivedAt: new Date(parseInt(internalDate)), @@ -74,5 +85,6 @@ export const parseAndFormatGmailMessage = ( attachments, messageFolderExternalIds: labelIds, labelIds, + isDraft, }; }; diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-get-messages.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-get-messages.service.ts index 78e0fb555c..c8138e2b59 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-get-messages.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-get-messages.service.ts @@ -145,6 +145,7 @@ export class ImapGetMessagesService { folderPath, folderExternalId, connectedAccount, + result.flags, ), ); } @@ -162,6 +163,7 @@ export class ImapGetMessagesService { folderPath: string, folderExternalId: string, connectedAccount: Pick, + flags?: Set, ): MessageWithParticipants { const fromAddresses = extractAddressesFromParsedEmail(parsed.from); const senderAddress = fromAddresses[0]?.address ?? ''; @@ -184,6 +186,7 @@ export class ImapGetMessagesService { })), participants: extractParticipantsFromParsedEmail(parsed), messageFolderExternalIds: [folderExternalId], + isDraft: flags?.has('\\Draft') ?? false, }; } } diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-message-parser.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-message-parser.service.ts index 350645440f..6e8a90d75e 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-message-parser.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-message-parser.service.ts @@ -6,6 +6,7 @@ import PostalMime, { type Email as ParsedEmail } from 'postal-mime'; export type MessageParseResult = { uid: number; parsed: ParsedEmail | null; + flags?: Set; error?: Error; }; @@ -40,7 +41,7 @@ export class ImapMessageParserService { const messages = await client.fetchAll( uidSet, - { uid: true, source: true }, + { uid: true, source: true, flags: true }, { uid: true }, ); @@ -80,22 +81,22 @@ export class ImapMessageParserService { private async parseMessage( message: FetchMessageObject, ): Promise { - const { uid, source } = message; + const { uid, source, flags } = message; if (!source) { this.logger.debug(`No source content for message UID ${uid}`); - return { uid, parsed: null }; + return { uid, parsed: null, flags }; } try { const parsed = await PostalMime.parse(source); - return { uid, parsed }; + return { uid, parsed, flags }; } catch (error) { this.logger.error(`Failed to parse message UID ${uid}: ${error.message}`); - return { uid, parsed: null, error: error as Error }; + return { uid, parsed: null, flags, error: error as Error }; } } diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/inbound-email/services/inbound-email-parser.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/inbound-email/services/inbound-email-parser.service.ts index f7265027c7..02e4898701 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/inbound-email/services/inbound-email-parser.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/inbound-email/services/inbound-email-parser.service.ts @@ -35,6 +35,7 @@ export class InboundEmailParserService { direction: MessageDirection.INCOMING, attachments: [], participants: extractParticipantsFromParsedEmail(parsedEmail), + isDraft: false, }; } } diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.service.spec.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.service.spec.ts index d33add5c7b..426ccaccef 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.service.spec.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.service.spec.ts @@ -99,6 +99,7 @@ describe('Microsoft get messages service', () => { role: MessageParticipantRole.TO, }, ], + isDraft: false, attachments: [], messageFolderExternalIds: responseExample1.body.parentFolderId ? [responseExample1.body.parentFolderId] @@ -145,6 +146,7 @@ describe('Microsoft get messages service', () => { role: MessageParticipantRole.CC, }, ], + isDraft: false, attachments: [], messageFolderExternalIds: responseExample2.body.parentFolderId ? [responseExample2.body.parentFolderId] @@ -188,6 +190,7 @@ describe('Microsoft get messages service', () => { role: MessageParticipantRole.FROM, }, ], + isDraft: false, attachments: [], messageFolderExternalIds: responseExample.body.parentFolderId ? [responseExample.body.parentFolderId] diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.service.ts index da4bc0f6ac..ff67a241e0 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.service.ts @@ -163,6 +163,7 @@ export class MicrosoftGetMessagesService { messageFolderExternalIds: response.parentFolderId ? [response.parentFolderId] : [], + isDraft: response.isDraft ?? false, }; }); diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-message.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-message.service.ts index e71ce58120..59adb7343b 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-message.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-message.service.ts @@ -24,6 +24,7 @@ type MessageAccumulator = { | 'receivedAt' | 'text' | 'messageThreadId' + | 'isDraft' >; threadToCreate?: Pick; messageChannelMessageAssociationToCreate?: Pick< @@ -168,6 +169,7 @@ export class MessagingMessageService { receivedAt: message.receivedAt, text: message.text, messageThreadId, + isDraft: message.isDraft, }; messageAccumulator.messageToCreate = messageToCreate; diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.spec.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.spec.ts index 46bffe59aa..bdc879c8c9 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.spec.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.spec.ts @@ -55,6 +55,7 @@ describe('MessagingSaveMessagesAndEnqueueContactCreationService', () => { text: 'Test content 1', receivedAt: new Date(), attachments: [], + isDraft: false, messageThreadExternalId: 'thread-1', direction: MessageDirection.OUTGOING, participants: [ @@ -77,6 +78,7 @@ describe('MessagingSaveMessagesAndEnqueueContactCreationService', () => { text: 'Test content 2', receivedAt: new Date(), attachments: [], + isDraft: false, messageThreadExternalId: 'thread-1', direction: MessageDirection.INCOMING, participants: [ @@ -329,4 +331,40 @@ describe('MessagingSaveMessagesAndEnqueueContactCreationService', () => { }, ); }); + + it('should not create contacts for unsent drafts', async () => { + await service.saveMessagesAndEnqueueContactCreation( + [ + { + ...mockMessages[0], + isDraft: true, + participants: [ + { + role: MessageParticipantRole.FROM, + handle: 'test@example.com', + displayName: 'Test User', + }, + { + role: MessageParticipantRole.TO, + handle: 'prospect@company.com', + displayName: 'Prospect', + }, + ], + }, + ], + mockMessageChannel, + mockConnectedAccount, + workspaceId, + ); + + expect(messageQueueService.add).toHaveBeenCalledWith( + CreateCompanyAndContactJob.name, + { + workspaceId, + connectedAccount: mockConnectedAccount, + source: FieldActorSource.EMAIL, + contactsToCreate: [], + }, + ); + }); }); diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.ts index abbd649965..dc833e5e69 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.ts @@ -96,7 +96,10 @@ export class MessagingSaveMessagesAndEnqueueContactCreationService { messageChannel.excludeNonProfessionalEmails && !isWorkEmail(participant.handle); + // Drafts are outgoing, so don't turn recipients of an + // unsent email into CRM contacts. const shouldCreateContact = + !message.isDraft && !!participant.handle && !isParticipantConnectedAccount && !isExcludedByNonProfessionalEmails && diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/utils/__mocks__/messages.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/utils/__mocks__/messages.ts index 761a876235..2058b389fa 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/utils/__mocks__/messages.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/utils/__mocks__/messages.ts @@ -25,6 +25,7 @@ export const messagingGetMessagesServiceGetMessages = [ }, ], attachments: [], + isDraft: false, }, { externalId: 'AA-work-emails-external', @@ -47,6 +48,7 @@ export const messagingGetMessagesServiceGetMessages = [ }, ], attachments: [], + isDraft: false, }, { externalId: 'AA-personal-emails', @@ -69,5 +71,6 @@ export const messagingGetMessagesServiceGetMessages = [ }, ], attachments: [], + isDraft: false, }, ] satisfies MessageWithParticipants[]; diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/utils/__tests__/filter-emails.util.spec.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/utils/__tests__/filter-emails.util.spec.ts index 62ccdcf477..bb7c941e1b 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/utils/__tests__/filter-emails.util.spec.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/utils/__tests__/filter-emails.util.spec.ts @@ -93,6 +93,7 @@ describe('filterEmails', () => { }, ], attachments: [], + isDraft: false, }, { externalId: 'support-message', @@ -110,6 +111,7 @@ describe('filterEmails', () => { }, ], attachments: [], + isDraft: false, }, { externalId: 'regular-message', @@ -127,6 +129,7 @@ describe('filterEmails', () => { }, ], attachments: [], + isDraft: false, }, ]; @@ -155,6 +158,7 @@ describe('filterEmails', () => { }, ], attachments: [], + isDraft: false, }, ]; @@ -176,6 +180,7 @@ describe('filterEmails', () => { direction: MessageDirection.INCOMING, participants: undefined as any, attachments: [], + isDraft: false, }, ]; @@ -203,6 +208,7 @@ describe('filterEmails', () => { }, ], attachments: [], + isDraft: false, }, ]; @@ -236,6 +242,7 @@ describe('filterEmails', () => { }, ], attachments: [], + isDraft: false, }, { externalId: 'alias-sent-message', @@ -258,6 +265,7 @@ describe('filterEmails', () => { }, ], attachments: [], + isDraft: false, }, { externalId: 'reply-from-john', @@ -280,6 +288,7 @@ describe('filterEmails', () => { }, ], attachments: [], + isDraft: false, }, { externalId: 'incoming-from-noreply', @@ -302,6 +311,7 @@ describe('filterEmails', () => { }, ], attachments: [], + isDraft: false, }, ]; diff --git a/packages/twenty-server/src/modules/messaging/message-outbound-manager/drivers/email-group/services/email-group-message-outbound.service.ts b/packages/twenty-server/src/modules/messaging/message-outbound-manager/drivers/email-group/services/email-group-message-outbound.service.ts index a847c5c148..d73d9bfadf 100644 --- a/packages/twenty-server/src/modules/messaging/message-outbound-manager/drivers/email-group/services/email-group-message-outbound.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-outbound-manager/drivers/email-group/services/email-group-message-outbound.service.ts @@ -71,6 +71,13 @@ export class EmailGroupMessageOutboundService implements MessageOutboundDriver { ); } + async sendDraft(): Promise { + throw new MessageChannelException( + 'Email handle channels do not support drafts.', + MessageChannelExceptionCode.INVALID_MESSAGE_CHANNEL_INPUT, + ); + } + private async resolveEmailingDomain( connectedAccount: ConnectedAccountEntity, ): Promise { diff --git a/packages/twenty-server/src/modules/messaging/message-outbound-manager/drivers/gmail/services/gmail-message-outbound.service.ts b/packages/twenty-server/src/modules/messaging/message-outbound-manager/drivers/gmail/services/gmail-message-outbound.service.ts index 201d2f3be9..c1f8f34387 100644 --- a/packages/twenty-server/src/modules/messaging/message-outbound-manager/drivers/gmail/services/gmail-message-outbound.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-outbound-manager/drivers/gmail/services/gmail-message-outbound.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, Logger } from '@nestjs/common'; import { isNonEmptyString } from '@sniptt/guards'; import { type gmail_v1, google } from 'googleapis'; @@ -17,6 +17,8 @@ import { toMailComposerOptions } from 'src/modules/messaging/message-outbound-ma @Injectable() export class GmailMessageOutboundService implements MessageOutboundDriver { + private readonly logger = new Logger(GmailMessageOutboundService.name); + constructor( private readonly googleOAuth2ClientProvider: GoogleOAuth2ClientProvider, ) {} @@ -67,6 +69,78 @@ export class GmailMessageOutboundService implements MessageOutboundDriver { }); } + async sendDraft( + draftExternalId: string, + sendMessageInput: SendMessageInput, + connectedAccount: ConnectedAccountEntity, + ): Promise { + const sendResult = await this.sendMessage( + sendMessageInput, + connectedAccount, + ); + + try { + await this.deleteDraftByMessageId(connectedAccount, draftExternalId); + } catch (error) { + this.logger.warn( + `Failed to delete Gmail draft for message ${draftExternalId} after send: ${error}`, + ); + } + + return sendResult; + } + + private async deleteDraftByMessageId( + connectedAccount: ConnectedAccountEntity, + messageId: string, + ): Promise { + const oAuth2Client = await this.googleOAuth2ClientProvider.getClient( + connectedAccount.id, + ); + + const gmailClient = google.gmail({ version: 'v1', auth: oAuth2Client }); + + const draftId = await this.findDraftIdByMessageId(gmailClient, messageId); + + if (isDefined(draftId)) { + await gmailClient.users.drafts.delete({ userId: 'me', id: draftId }); + + return; + } + + this.logger.warn( + `No Gmail draft found for message ${messageId}; skipping delete`, + ); + } + + private async findDraftIdByMessageId( + gmailClient: gmail_v1.Gmail, + messageId: string, + ): Promise { + let pageToken: string | undefined = undefined; + + do { + const { data }: { data: gmail_v1.Schema$ListDraftsResponse } = + await gmailClient.users.drafts.list({ + userId: 'me', + maxResults: 500, + pageToken, + }); + + const draft = (data.drafts ?? []).find( + (currentDraft) => currentDraft.message?.id === messageId, + ); + + if (isDefined(draft?.id)) { + return draft.id; + } + + pageToken = data.nextPageToken ?? undefined; + } while (isDefined(pageToken)); + + return undefined; + } + private async composeGmailMessage( connectedAccount: ConnectedAccountEntity, sendMessageInput: SendMessageInput, diff --git a/packages/twenty-server/src/modules/messaging/message-outbound-manager/drivers/imap/services/imap-smtp-message-outbound.service.ts b/packages/twenty-server/src/modules/messaging/message-outbound-manager/drivers/imap/services/imap-smtp-message-outbound.service.ts index e23042ae6c..13ff820410 100644 --- a/packages/twenty-server/src/modules/messaging/message-outbound-manager/drivers/imap/services/imap-smtp-message-outbound.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-outbound-manager/drivers/imap/services/imap-smtp-message-outbound.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, Logger } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import MailComposer from 'nodemailer/lib/mail-composer'; @@ -13,6 +13,7 @@ import { type ConnectedAccountEntity } from 'src/engine/metadata-modules/connect import { ImapClientProvider } from 'src/modules/messaging/message-import-manager/drivers/imap/providers/imap-client.provider'; import { ImapFindDraftsFolderService } from 'src/modules/messaging/message-import-manager/drivers/imap/services/imap-find-drafts-folder.service'; import { getImapFolderPath } from 'src/modules/messaging/message-import-manager/drivers/imap/utils/get-imap-folder-path.util'; +import { parseMessageId } from 'src/modules/messaging/message-import-manager/drivers/imap/utils/parse-message-id.util'; import { SmtpClientProvider } from 'src/modules/messaging/message-import-manager/drivers/smtp/providers/smtp-client.provider'; import { type SendMessageInput } from 'src/modules/messaging/message-outbound-manager/types/send-message-input.type'; import { type SendMessageResult } from 'src/modules/messaging/message-outbound-manager/types/send-message-result.type'; @@ -21,6 +22,8 @@ import { toMailComposerOptions } from 'src/modules/messaging/message-outbound-ma @Injectable() export class ImapSmtpMessageOutboundService implements MessageOutboundDriver { + private readonly logger = new Logger(ImapSmtpMessageOutboundService.name); + constructor( private readonly smtpClientProvider: SmtpClientProvider, private readonly imapClientProvider: ImapClientProvider, @@ -131,6 +134,56 @@ export class ImapSmtpMessageOutboundService implements MessageOutboundDriver { } } + async sendDraft( + draftExternalId: string, + sendMessageInput: SendMessageInput, + connectedAccount: ConnectedAccountEntity, + ): Promise { + const sendResult = await this.sendMessage( + sendMessageInput, + connectedAccount, + ); + + try { + await this.deleteDraft(draftExternalId, connectedAccount); + } catch (error) { + this.logger.warn( + `Failed to delete IMAP draft ${draftExternalId} after send: ${error}`, + ); + } + + return sendResult; + } + + async deleteDraft( + externalId: string, + connectedAccount: ConnectedAccountEntity, + ): Promise { + const parsedMessageId = parseMessageId(externalId); + + if (!isDefined(parsedMessageId)) { + throw new Error( + `Could not resolve IMAP drafts folder and uid from external id ${externalId}`, + ); + } + + const imapClient = await this.imapClientProvider.getClient( + connectedAccount.id, + ); + + try { + const lock = await imapClient.getMailboxLock(parsedMessageId.folder); + + try { + await imapClient.messageDelete(`${parsedMessageId.uid}`, { uid: true }); + } finally { + lock.release(); + } + } finally { + await this.imapClientProvider.closeClient(imapClient); + } + } + private async compileRawMessage( from: string, sendMessageInput: SendMessageInput, diff --git a/packages/twenty-server/src/modules/messaging/message-outbound-manager/drivers/microsoft/services/microsoft-message-outbound.service.ts b/packages/twenty-server/src/modules/messaging/message-outbound-manager/drivers/microsoft/services/microsoft-message-outbound.service.ts index 5cf1a371e6..05cade5416 100644 --- a/packages/twenty-server/src/modules/messaging/message-outbound-manager/drivers/microsoft/services/microsoft-message-outbound.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-outbound-manager/drivers/microsoft/services/microsoft-message-outbound.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, Logger } from '@nestjs/common'; import { type MessageOutboundDriver } from 'src/modules/messaging/message-outbound-manager/interfaces/message-outbound-driver.interface'; @@ -12,6 +12,8 @@ import { isDefined } from 'twenty-shared/utils'; @Injectable() export class MicrosoftMessageOutboundService implements MessageOutboundDriver { + private readonly logger = new Logger(MicrosoftMessageOutboundService.name); + constructor( private readonly microsoftOAuth2ClientProvider: MicrosoftOAuth2ClientProvider, ) {} @@ -50,6 +52,32 @@ export class MicrosoftMessageOutboundService implements MessageOutboundDriver { await this.createDraftMessage(microsoftClient, sendMessageInput); } + async sendDraft( + draftExternalId: string, + sendMessageInput: SendMessageInput, + connectedAccount: ConnectedAccountEntity, + ): Promise { + const sendResult = await this.sendMessage( + sendMessageInput, + connectedAccount, + ); + + const microsoftClient = await this.microsoftOAuth2ClientProvider.getClient( + connectedAccount.id, + ); + + await microsoftClient + .api(`/me/messages/${draftExternalId}`) + .delete() + .catch((error) => + this.logger.warn( + `Failed to delete Microsoft draft ${draftExternalId} after send: ${error}`, + ), + ); + + return sendResult; + } + private async createDraftMessage( microsoftClient: MicrosoftGraphClient, sendMessageInput: SendMessageInput, diff --git a/packages/twenty-server/src/modules/messaging/message-outbound-manager/dtos/send-email-output.dto.ts b/packages/twenty-server/src/modules/messaging/message-outbound-manager/dtos/send-email-output.dto.ts index 0a54a36ae1..786f823e30 100644 --- a/packages/twenty-server/src/modules/messaging/message-outbound-manager/dtos/send-email-output.dto.ts +++ b/packages/twenty-server/src/modules/messaging/message-outbound-manager/dtos/send-email-output.dto.ts @@ -7,4 +7,7 @@ export class SendEmailOutputDTO { @Field(() => String, { nullable: true }) error?: string; + + @Field(() => String, { nullable: true }) + messageThreadId?: string; } diff --git a/packages/twenty-server/src/modules/messaging/message-outbound-manager/dtos/send-email.input.ts b/packages/twenty-server/src/modules/messaging/message-outbound-manager/dtos/send-email.input.ts index 5f11b875b1..5f24549c46 100644 --- a/packages/twenty-server/src/modules/messaging/message-outbound-manager/dtos/send-email.input.ts +++ b/packages/twenty-server/src/modules/messaging/message-outbound-manager/dtos/send-email.input.ts @@ -32,6 +32,9 @@ export class SendEmailInput { @Field(() => String, { nullable: true }) inReplyTo?: string; + @Field(() => String, { nullable: true }) + draftMessageId?: string; + @Field(() => [SendEmailAttachmentInput], { nullable: true }) files?: SendEmailAttachmentInput[]; } diff --git a/packages/twenty-server/src/modules/messaging/message-outbound-manager/interfaces/message-outbound-driver.interface.ts b/packages/twenty-server/src/modules/messaging/message-outbound-manager/interfaces/message-outbound-driver.interface.ts index b2355b1473..84e005b390 100644 --- a/packages/twenty-server/src/modules/messaging/message-outbound-manager/interfaces/message-outbound-driver.interface.ts +++ b/packages/twenty-server/src/modules/messaging/message-outbound-manager/interfaces/message-outbound-driver.interface.ts @@ -12,4 +12,10 @@ export type MessageOutboundDriver = { sendMessageInput: SendMessageInput, connectedAccount: ConnectedAccountEntity, ): Promise; + + sendDraft( + draftExternalId: string, + sendMessageInput: SendMessageInput, + connectedAccount: ConnectedAccountEntity, + ): Promise; }; diff --git a/packages/twenty-server/src/modules/messaging/message-outbound-manager/messaging-send-manager.module.ts b/packages/twenty-server/src/modules/messaging/message-outbound-manager/messaging-send-manager.module.ts index 7dcb1a4e24..74d166e565 100644 --- a/packages/twenty-server/src/modules/messaging/message-outbound-manager/messaging-send-manager.module.ts +++ b/packages/twenty-server/src/modules/messaging/message-outbound-manager/messaging-send-manager.module.ts @@ -10,10 +10,12 @@ import { OAuth2ClientManagerModule } from 'src/modules/connected-account/oauth2- import { MessagingIMAPDriverModule } from 'src/modules/messaging/message-import-manager/drivers/imap/messaging-imap-driver.module'; import { MessagingSmtpDriverModule } from 'src/modules/messaging/message-import-manager/drivers/smtp/messaging-smtp-driver.module'; import { MessagingImportManagerModule } from 'src/modules/messaging/message-import-manager/messaging-import-manager.module'; +import { MessagingMessageCleanerModule } from 'src/modules/messaging/message-cleaner/messaging-message-cleaner.module'; import { EmailGroupMessageOutboundService } from 'src/modules/messaging/message-outbound-manager/drivers/email-group/services/email-group-message-outbound.service'; import { GmailMessageOutboundService } from 'src/modules/messaging/message-outbound-manager/drivers/gmail/services/gmail-message-outbound.service'; import { ImapSmtpMessageOutboundService } from 'src/modules/messaging/message-outbound-manager/drivers/imap/services/imap-smtp-message-outbound.service'; import { MicrosoftMessageOutboundService } from 'src/modules/messaging/message-outbound-manager/drivers/microsoft/services/microsoft-message-outbound.service'; +import { MessagingDraftSendService } from 'src/modules/messaging/message-outbound-manager/services/messaging-draft-send.service'; import { MessagingMessageOutboundService } from 'src/modules/messaging/message-outbound-manager/services/messaging-message-outbound.service'; import { SendEmailService } from 'src/modules/messaging/message-outbound-manager/services/send-email.service'; import { SentMessagePersistenceService } from 'src/modules/messaging/message-outbound-manager/services/sent-message-persistence.service'; @@ -24,6 +26,7 @@ import { SentMessagePersistenceService } from 'src/modules/messaging/message-out MessagingIMAPDriverModule, MessagingSmtpDriverModule, MessagingImportManagerModule, + MessagingMessageCleanerModule, EmailingModule, TypeOrmModule.forFeature([ MessageChannelEntity, @@ -37,12 +40,14 @@ import { SentMessagePersistenceService } from 'src/modules/messaging/message-out ImapSmtpMessageOutboundService, EmailGroupMessageOutboundService, MessagingMessageOutboundService, + MessagingDraftSendService, SendEmailService, SentMessagePersistenceService, provideWorkspaceScopedRepository(EmailingDomainEntity), ], exports: [ MessagingMessageOutboundService, + MessagingDraftSendService, SendEmailService, SentMessagePersistenceService, ], diff --git a/packages/twenty-server/src/modules/messaging/message-outbound-manager/resolvers/send-email.resolver.ts b/packages/twenty-server/src/modules/messaging/message-outbound-manager/resolvers/send-email.resolver.ts index 5ae14b2afe..91ba3de5fc 100644 --- a/packages/twenty-server/src/modules/messaging/message-outbound-manager/resolvers/send-email.resolver.ts +++ b/packages/twenty-server/src/modules/messaging/message-outbound-manager/resolvers/send-email.resolver.ts @@ -23,6 +23,8 @@ import { ConnectedAccountMetadataService } from 'src/engine/metadata-modules/con import { SendEmailOutputDTO } from 'src/modules/messaging/message-outbound-manager/dtos/send-email-output.dto'; import { SendEmailInput } from 'src/modules/messaging/message-outbound-manager/dtos/send-email.input'; import { SendEmailService } from 'src/modules/messaging/message-outbound-manager/services/send-email.service'; +import { isDefined } from 'twenty-shared/utils'; +import { isNonEmptyString } from '@sniptt/guards'; @MetadataResolver() @UsePipes(ResolverValidationPipe) @@ -79,26 +81,60 @@ export class SendEmailResolver { const { data } = result; - const sendResult = await this.sendEmailService.sendComposedEmail(data); + const sendResult = isDefined(input.draftMessageId) + ? await this.sendEmailService.sendComposedDraft( + data, + input.draftMessageId, + workspace.id, + ) + : await this.sendEmailService.sendComposedEmail(data); - if (data.shouldPersistMessage) { - await this.sendEmailService.persistSentMessage( - sendResult, - data, - workspace.id, + let messageThreadId: string | undefined; + + try { + if (data.shouldPersistMessage) { + await this.sendEmailService.persistSentMessage( + sendResult, + data, + workspace.id, + ); + } + + if (isDefined(input.draftMessageId)) { + await this.sendEmailService.deleteSentDraft( + input.draftMessageId, + input.connectedAccountId, + workspace.id, + ); + } + + const sentMessageExternalId = + sendResult.messageExternalId ?? sendResult.headerMessageId; + + messageThreadId = + isDefined(input.draftMessageId) && + isNonEmptyString(sentMessageExternalId) + ? await this.sendEmailService.getSentMessageThreadId( + sentMessageExternalId, + workspace.id, + ) + : undefined; + + const attachmentFileIds = (input.files ?? []).map((file) => file.id); + + if (attachmentFileIds.length > 0) { + await this.fileEmailAttachmentService.deleteFiles({ + fileIds: attachmentFileIds, + workspaceId: workspace.id, + }); + } + } catch (postSendError) { + this.logger.warn( + `Email sent but post-send cleanup failed (sync will recover): ${postSendError}`, ); } - const attachmentFileIds = (input.files ?? []).map((file) => file.id); - - if (attachmentFileIds.length > 0) { - await this.fileEmailAttachmentService.deleteFiles({ - fileIds: attachmentFileIds, - workspaceId: workspace.id, - }); - } - - return { success: true }; + return { success: true, messageThreadId }; } catch (error) { if (error instanceof ForbiddenException) { throw error; diff --git a/packages/twenty-server/src/modules/messaging/message-outbound-manager/services/messaging-draft-send.service.ts b/packages/twenty-server/src/modules/messaging/message-outbound-manager/services/messaging-draft-send.service.ts new file mode 100644 index 0000000000..65b2369781 --- /dev/null +++ b/packages/twenty-server/src/modules/messaging/message-outbound-manager/services/messaging-draft-send.service.ts @@ -0,0 +1,171 @@ +import { Injectable } from '@nestjs/common'; + +import { isNonEmptyString } from '@sniptt/guards'; +import { isDefined } from 'twenty-shared/utils'; +import { In } from 'typeorm'; + +import { type ConnectedAccountEntity } from 'src/engine/metadata-modules/connected-account/entities/connected-account.entity'; +import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; +import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util'; +import { type MessageChannelMessageAssociationWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel-message-association.workspace-entity'; +import { type MessageChannelWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity'; +import { MessagingMessageCleanerService } from 'src/modules/messaging/message-cleaner/services/messaging-message-cleaner.service'; +import { MessagingMessageOutboundService } from 'src/modules/messaging/message-outbound-manager/services/messaging-message-outbound.service'; +import { type SendMessageInput } from 'src/modules/messaging/message-outbound-manager/types/send-message-input.type'; +import { type SendMessageResult } from 'src/modules/messaging/message-outbound-manager/types/send-message-result.type'; + +@Injectable() +export class MessagingDraftSendService { + constructor( + private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager, + private readonly messageOutboundService: MessagingMessageOutboundService, + private readonly messageCleanerService: MessagingMessageCleanerService, + ) {} + + async sendDraftMessage({ + draftMessageId, + sendMessageInput, + connectedAccount, + workspaceId, + }: { + draftMessageId: string; + sendMessageInput: SendMessageInput; + connectedAccount: ConnectedAccountEntity; + workspaceId: string; + }): Promise { + const draftAssociation = await this.resolveDraftAssociation( + draftMessageId, + connectedAccount.id, + workspaceId, + ); + + if (!isDefined(draftAssociation)) { + throw new Error( + `Could not find a synced draft to send for message ${draftMessageId}`, + ); + } + + return this.messageOutboundService.sendDraft( + draftAssociation.messageExternalId, + sendMessageInput, + connectedAccount, + ); + } + + async getSentMessageThreadId({ + messageExternalId, + workspaceId, + }: { + messageExternalId: string; + workspaceId: string; + }): Promise { + const authContext = buildSystemAuthContext(workspaceId); + + return this.globalWorkspaceOrmManager.executeInWorkspaceContext( + async () => { + const messageChannelMessageAssociationRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageChannelMessageAssociation', + ); + + const association = + await messageChannelMessageAssociationRepository.findOne({ + where: { messageExternalId }, + relations: ['message'], + }); + + return association?.message?.messageThreadId ?? undefined; + }, + authContext, + { lite: true }, + ); + } + + async deleteSentDraft({ + draftMessageId, + connectedAccountId, + workspaceId, + }: { + draftMessageId: string; + connectedAccountId: string; + workspaceId: string; + }): Promise { + const draftAssociation = await this.resolveDraftAssociation( + draftMessageId, + connectedAccountId, + workspaceId, + ); + + if (!isDefined(draftAssociation)) { + return; + } + + await this.messageCleanerService.deleteMessagesChannelMessageAssociationsAndRelatedOrphans( + { + workspaceId, + messageExternalIds: [draftAssociation.messageExternalId], + messageChannelId: draftAssociation.messageChannelId, + }, + ); + } + + // Scoped to the caller's own channels so a member cannot act on another + // member's draft by passing its message id. + private async resolveDraftAssociation( + draftMessageId: string, + connectedAccountId: string, + workspaceId: string, + ): Promise<{ messageExternalId: string; messageChannelId: string } | null> { + const authContext = buildSystemAuthContext(workspaceId); + + const associations = + await this.globalWorkspaceOrmManager.executeInWorkspaceContext( + async () => { + const messageChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageChannel', + ); + + const channels = await messageChannelRepository.find({ + where: { connectedAccountId }, + }); + + const channelIds = channels.map((channel) => channel.id); + + if (channelIds.length === 0) { + return []; + } + + const messageChannelMessageAssociationRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageChannelMessageAssociation', + ); + + return messageChannelMessageAssociationRepository.find({ + where: { + messageId: draftMessageId, + messageChannelId: In(channelIds), + }, + }); + }, + authContext, + { lite: true }, + ); + + const association = associations.find((currentAssociation) => + isNonEmptyString(currentAssociation.messageExternalId), + ); + + if (!association || !isNonEmptyString(association.messageExternalId)) { + return null; + } + + return { + messageExternalId: association.messageExternalId, + messageChannelId: association.messageChannelId, + }; + } +} diff --git a/packages/twenty-server/src/modules/messaging/message-outbound-manager/services/messaging-message-outbound.service.ts b/packages/twenty-server/src/modules/messaging/message-outbound-manager/services/messaging-message-outbound.service.ts index e32629b63f..2ee0653102 100644 --- a/packages/twenty-server/src/modules/messaging/message-outbound-manager/services/messaging-message-outbound.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-outbound-manager/services/messaging-message-outbound.service.ts @@ -93,4 +93,43 @@ export class MessagingMessageOutboundService { ); } } + + public async sendDraft( + draftExternalId: string, + sendMessageInput: SendMessageInput, + connectedAccount: ConnectedAccountEntity, + ): Promise { + switch (connectedAccount.provider) { + case ConnectedAccountProvider.GOOGLE: + return this.gmailMessageOutboundService.sendDraft( + draftExternalId, + sendMessageInput, + connectedAccount, + ); + case ConnectedAccountProvider.MICROSOFT: + return this.microsoftMessageOutboundService.sendDraft( + draftExternalId, + sendMessageInput, + connectedAccount, + ); + case ConnectedAccountProvider.IMAP_SMTP_CALDAV: + return this.imapSmtpMessageOutboundService.sendDraft( + draftExternalId, + sendMessageInput, + connectedAccount, + ); + case ConnectedAccountProvider.EMAIL_GROUP: + case ConnectedAccountProvider.OIDC: + case ConnectedAccountProvider.SAML: + case ConnectedAccountProvider.APP: + throw new Error( + `Provider ${connectedAccount.provider} does not support sending drafts`, + ); + default: + assertUnreachable( + connectedAccount.provider, + `Provider ${connectedAccount.provider} not supported for sending drafts`, + ); + } + } } diff --git a/packages/twenty-server/src/modules/messaging/message-outbound-manager/services/send-email.service.ts b/packages/twenty-server/src/modules/messaging/message-outbound-manager/services/send-email.service.ts index a30ab4b042..2bfce450bf 100644 --- a/packages/twenty-server/src/modules/messaging/message-outbound-manager/services/send-email.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-outbound-manager/services/send-email.service.ts @@ -1,8 +1,10 @@ import { Injectable, Logger } from '@nestjs/common'; import { type ComposedEmail } from 'src/engine/core-modules/tool/tools/email-tool/types/composed-email.type'; +import { MessagingDraftSendService } from 'src/modules/messaging/message-outbound-manager/services/messaging-draft-send.service'; import { MessagingMessageOutboundService } from 'src/modules/messaging/message-outbound-manager/services/messaging-message-outbound.service'; import { SentMessagePersistenceService } from 'src/modules/messaging/message-outbound-manager/services/sent-message-persistence.service'; +import { type SendMessageInput } from 'src/modules/messaging/message-outbound-manager/types/send-message-input.type'; import { type SendMessageResult } from 'src/modules/messaging/message-outbound-manager/types/send-message-result.type'; @Injectable() @@ -11,27 +13,67 @@ export class SendEmailService { constructor( private readonly messageOutboundService: MessagingMessageOutboundService, + private readonly messagingDraftSendService: MessagingDraftSendService, private readonly sentMessagePersistenceService: SentMessagePersistenceService, ) {} async sendComposedEmail(data: ComposedEmail): Promise { return this.messageOutboundService.sendMessage( - { - to: data.recipients.to, - cc: data.recipients.cc.length > 0 ? data.recipients.cc : undefined, - bcc: data.recipients.bcc.length > 0 ? data.recipients.bcc : undefined, - subject: data.sanitizedSubject, - body: data.plainTextBody, - html: data.sanitizedHtmlBody, - attachments: data.attachments, - inReplyTo: data.inReplyTo, - threadExternalId: data.threadExternalId, - references: data.references, - }, + this.toSendMessageInput(data), data.connectedAccount, ); } + async sendComposedDraft( + data: ComposedEmail, + draftMessageId: string, + workspaceId: string, + ): Promise { + return this.messagingDraftSendService.sendDraftMessage({ + draftMessageId, + sendMessageInput: this.toSendMessageInput(data), + connectedAccount: data.connectedAccount, + workspaceId, + }); + } + + async deleteSentDraft( + draftMessageId: string, + connectedAccountId: string, + workspaceId: string, + ): Promise { + await this.messagingDraftSendService.deleteSentDraft({ + draftMessageId, + connectedAccountId, + workspaceId, + }); + } + + async getSentMessageThreadId( + messageExternalId: string, + workspaceId: string, + ): Promise { + return this.messagingDraftSendService.getSentMessageThreadId({ + messageExternalId, + workspaceId, + }); + } + + private toSendMessageInput(data: ComposedEmail): SendMessageInput { + return { + to: data.recipients.to, + cc: data.recipients.cc.length > 0 ? data.recipients.cc : undefined, + bcc: data.recipients.bcc.length > 0 ? data.recipients.bcc : undefined, + subject: data.sanitizedSubject, + body: data.plainTextBody, + html: data.sanitizedHtmlBody, + attachments: data.attachments, + inReplyTo: data.inReplyTo, + threadExternalId: data.threadExternalId, + references: data.references, + }; + } + async persistSentMessage( sendResult: SendMessageResult, data: ComposedEmail, diff --git a/packages/twenty-server/src/modules/messaging/message-outbound-manager/utils/format-sent-message.util.ts b/packages/twenty-server/src/modules/messaging/message-outbound-manager/utils/format-sent-message.util.ts index 83150b6c46..0098b70f63 100644 --- a/packages/twenty-server/src/modules/messaging/message-outbound-manager/utils/format-sent-message.util.ts +++ b/packages/twenty-server/src/modules/messaging/message-outbound-manager/utils/format-sent-message.util.ts @@ -55,5 +55,6 @@ export const formatSentMessage = ( direction: MessageDirection.OUTGOING, attachments: [], participants, + isDraft: false, }; }; diff --git a/packages/twenty-shared/src/metadata/constants/standard-object.constant.ts b/packages/twenty-shared/src/metadata/constants/standard-object.constant.ts index cc0c07d71b..eb497d4081 100644 --- a/packages/twenty-shared/src/metadata/constants/standard-object.constant.ts +++ b/packages/twenty-shared/src/metadata/constants/standard-object.constant.ts @@ -1480,6 +1480,9 @@ export const STANDARD_OBJECTS = { deliveryStatus: { universalIdentifier: '209254fa-2b89-429d-a72a-c401c4bd5a78', }, + isDraft: { + universalIdentifier: '20202020-4d3a-4b6e-9c1f-2a5e7b9d0c34', + }, createdBy: { universalIdentifier: '6e52bde4-ed41-4462-aa70-121e496270b4', },