diff --git a/packages/twenty-front/src/modules/activities/emails/components/EmailThreadMessage.tsx b/packages/twenty-front/src/modules/activities/emails/components/EmailThreadMessage.tsx index cc234a82e..dd179fba1 100644 --- a/packages/twenty-front/src/modules/activities/emails/components/EmailThreadMessage.tsx +++ b/packages/twenty-front/src/modules/activities/emails/components/EmailThreadMessage.tsx @@ -1,10 +1,10 @@ -import React, { useState } from 'react'; +import { useState } from 'react'; import styled from '@emotion/styled'; import { EmailThreadMessageBody } from '@/activities/emails/components/EmailThreadMessageBody'; import { EmailThreadMessageBodyPreview } from '@/activities/emails/components/EmailThreadMessageBodyPreview'; import { EmailThreadMessageSender } from '@/activities/emails/components/EmailThreadMessageSender'; -import { MockedEmailUser } from '@/activities/emails/mocks/mockedEmailThreads'; +import { EmailThreadMessageParticipant } from '@/activities/emails/types/EmailThreadMessageParticipant'; const StyledThreadMessage = styled.div` border-bottom: 1px solid ${({ theme }) => theme.border.color.light}; @@ -22,21 +22,52 @@ const StyledThreadMessageHeader = styled.div` `; type EmailThreadMessageProps = { - id: string; body: string; sentAt: string; - from: MockedEmailUser; - to: MockedEmailUser[]; + participants: EmailThreadMessageParticipant[]; +}; + +const getDisplayNameFromParticipant = ( + participant: EmailThreadMessageParticipant, +) => { + if (participant.person) { + return `${participant.person?.name?.firstName} ${participant.person?.name?.lastName}`; + } + + if (participant.workspaceMember) { + return `${participant.workspaceMember?.name?.firstName} ${participant.workspaceMember?.name?.lastName}`; + } + + if (participant.displayName) { + return participant.displayName; + } + + if (participant.handle) { + return participant.handle; + } + + return 'Unknown'; }; export const EmailThreadMessage = ({ body, sentAt, - from, + participants, }: EmailThreadMessageProps) => { - const { displayName, avatarUrl } = from; const [isOpen, setIsOpen] = useState(false); + const from = participants.find((participant) => participant.role === 'from'); + const to = participants.filter((participant) => participant.role === 'to'); + + if (!from || to.length === 0) { + return null; + } + + const displayName = getDisplayNameFromParticipant(from); + + const avatarUrl = + from.person?.avatarUrl ?? from.workspaceMember?.avatarUrl ?? ''; + return ( setIsOpen(!isOpen)}> diff --git a/packages/twenty-front/src/modules/activities/emails/mocks/mockedEmailThreads.ts b/packages/twenty-front/src/modules/activities/emails/mocks/mockedEmailThreads.ts index 1de2452c6..8809b8b9e 100644 --- a/packages/twenty-front/src/modules/activities/emails/mocks/mockedEmailThreads.ts +++ b/packages/twenty-front/src/modules/activities/emails/mocks/mockedEmailThreads.ts @@ -1,31 +1,13 @@ -import { DateTime } from 'luxon'; - import { Scalars, TimelineThread } from '~/generated/graphql'; export type MockedThread = { id: string; } & TimelineThread; -export type MockedEmailUser = { - avatarUrl: string; - displayName: string; - workspaceMemberId?: string; - personId?: string; -}; - -export type MockedMessage = { - id: string; - from: MockedEmailUser; - to: MockedEmailUser[]; - subject: string; - body: string; - sentAt: string; -}; - export const mockedEmailThreads: MockedThread[] = [ { __typename: 'TimelineThread', - id: '1', + id: '4e88ec1f-a386-4235-bd82-98f25f6d557e', body: 'This is a test email' as Scalars['String'], numberOfMessagesInThread: 5 as Scalars['Float'], read: true as Scalars['Boolean'], @@ -36,7 +18,7 @@ export const mockedEmailThreads: MockedThread[] = [ }, { __typename: 'TimelineThread', - id: '2', + id: '4e88ec1f-a386-4235-bd82-98f25f6d557e', body: 'This is a second test email' as Scalars['String'], numberOfMessagesInThread: 5 as Scalars['Float'], read: true as Scalars['Boolean'], @@ -46,65 +28,3 @@ export const mockedEmailThreads: MockedThread[] = [ subject: 'Test email number 2' as Scalars['String'], }, ]; - -export const mockedMessagesByThread: Map = new Map([ - [ - '1', - Array.from({ length: 5 }).map((_, i) => ({ - id: `id${i + 1}`, - from: { - avatarUrl: '', - displayName: `User ${i + 1}`, - workspaceMemberId: `workspaceMemberId${i + 1}`, - personId: `personId${i + 1}`, - }, - to: [ - { - avatarUrl: 'https://favicon.twenty.com/qonto.com', - displayName: `User ${i + 2}`, - workspaceMemberId: `workspaceMemberId${i + 1}`, - personId: `personId${i + 2}`, - }, - ], - subject: `Subject ${i + 1}`, - body: `Body ${ - i + 1 - }. I am testing a very long body. I am adding more text. -I also want to test a new line. To see if it works. - -I am adding a new paragraph. - -Thomas`, - sentAt: DateTime.fromFormat('2021-03-12', 'yyyy-MM-dd').toISO() ?? '', - })), - ], - [ - '2', - Array.from({ length: 5 }).map((_, i) => ({ - id: `id${i + 10}`, - from: { - avatarUrl: '', - displayName: `Other user ${i + 1}`, - workspaceMemberId: `workspaceMemberId${i + 1}`, - personId: `personId${i + 1}`, - }, - to: [ - { - avatarUrl: 'https://favicon.twenty.com/qonto.com', - displayName: `Other user ${i + 2}`, - workspaceMemberId: `workspaceMemberId${i + 1}`, - personId: `personId${i + 2}`, - }, - ], - subject: `Subject ${i + 1}`, - body: `Body ${ - i + 1 - }. Hello, I am testing a very long body. I am adding more text. - -I am adding a new paragraph. - -Thomas`, - sentAt: DateTime.fromFormat('2021-03-12', 'yyyy-MM-dd').toISO() ?? '', - })), - ], -]); diff --git a/packages/twenty-front/src/modules/activities/emails/right-drawer/components/RightDrawerEmailThread.tsx b/packages/twenty-front/src/modules/activities/emails/right-drawer/components/RightDrawerEmailThread.tsx index 995db3da6..e89be4151 100644 --- a/packages/twenty-front/src/modules/activities/emails/right-drawer/components/RightDrawerEmailThread.tsx +++ b/packages/twenty-front/src/modules/activities/emails/right-drawer/components/RightDrawerEmailThread.tsx @@ -4,8 +4,10 @@ import { useRecoilValue } from 'recoil'; import { EmailThreadHeader } from '@/activities/emails/components/EmailThreadHeader'; import { EmailThreadMessage } from '@/activities/emails/components/EmailThreadMessage'; -import { mockedMessagesByThread } from '@/activities/emails/mocks/mockedEmailThreads'; import { viewableEmailThreadState } from '@/activities/emails/state/viewableEmailThreadState'; +import { EmailThreadMessage as EmailThreadMessageType } from '@/activities/emails/types/EmailThreadMessage'; +import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; const StyledContainer = styled.div` box-sizing: border-box; @@ -20,27 +22,37 @@ const StyledContainer = styled.div` export const RightDrawerEmailThread = () => { const viewableEmailThread = useRecoilValue(viewableEmailThreadState); + const { records: messages } = useFindManyRecords({ + depth: 3, + filter: { + messageThreadId: { + eq: viewableEmailThread?.id, + }, + }, + objectNameSingular: CoreObjectNameSingular.Message, + orderBy: { + receivedAt: 'DescNullsLast', + }, + skip: !viewableEmailThread, + useRecordsWithoutConnection: true, + }); + if (!viewableEmailThread) { return null; } - const mockedMessages = - mockedMessagesByThread.get(viewableEmailThread.id) ?? []; - return ( - {mockedMessages.map((message) => ( + {messages.map((message) => ( ))} diff --git a/packages/twenty-front/src/modules/activities/emails/types/EmailThreadMessage.ts b/packages/twenty-front/src/modules/activities/emails/types/EmailThreadMessage.ts new file mode 100644 index 000000000..c86b40ece --- /dev/null +++ b/packages/twenty-front/src/modules/activities/emails/types/EmailThreadMessage.ts @@ -0,0 +1,8 @@ +import { EmailThreadMessageParticipant } from '@/activities/emails/types/EmailThreadMessageParticipant'; + +export type EmailThreadMessage = { + id: string; + text: string; + receivedAt: string; + messageParticipants: EmailThreadMessageParticipant[]; +}; diff --git a/packages/twenty-front/src/modules/activities/emails/types/EmailThreadMessageParticipant.ts b/packages/twenty-front/src/modules/activities/emails/types/EmailThreadMessageParticipant.ts new file mode 100644 index 000000000..8f1367f54 --- /dev/null +++ b/packages/twenty-front/src/modules/activities/emails/types/EmailThreadMessageParticipant.ts @@ -0,0 +1,10 @@ +import { Person } from '@/people/types/Person'; +import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember'; + +export type EmailThreadMessageParticipant = { + displayName: string; + handle: string; + role: string; + person: Person; + workspaceMember: WorkspaceMember; +}; diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useMapFieldMetadataToGraphQLQuery.test.tsx b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useMapFieldMetadataToGraphQLQuery.test.tsx index 8e9940e6d..a0d7f63e7 100644 --- a/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useMapFieldMetadataToGraphQLQuery.test.tsx +++ b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useMapFieldMetadataToGraphQLQuery.test.tsx @@ -27,12 +27,32 @@ const getOneToManyRelation = () => { label url } +accountOwner + { + id + } linkedinLink { label url } +attachments + { + edges { + node { + id + } + } + } domainName +opportunities + { + edges { + node { + id + } + } + } annualRecurringRevenue { amountMicros @@ -41,6 +61,30 @@ domainName createdAt address updatedAt +activityTargets + { + edges { + node { + id + } + } + } +favorites + { + edges { + node { + id + } + } + } +people + { + edges { + node { + id + } + } + } name accountOwnerId employees @@ -89,9 +133,17 @@ const getOneToManyFromRelationField = () => { personId pointOfContactId updatedAt +company + { + id + } companyId pipelineStepId probability +pipelineStep + { + id + } closeDate amount { @@ -100,6 +152,14 @@ closeDate } id createdAt +pointOfContact + { + id + } +person + { + id + } } } }`, diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery.ts b/packages/twenty-front/src/modules/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery.ts index abf182aaa..5dced82b1 100644 --- a/packages/twenty-front/src/modules/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery.ts +++ b/packages/twenty-front/src/modules/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery.ts @@ -48,7 +48,6 @@ export const useMapFieldMetadataToGraphQLQuery = () => { { id ${(relationMetadataItem?.fields ?? []) - .filter((field) => field.type !== 'RELATION') .map((field) => mapFieldMetadataToGraphQLQuery(field, maxDepthForRelations - 1), ) @@ -68,7 +67,6 @@ export const useMapFieldMetadataToGraphQLQuery = () => { { id ${(relationMetadataItem?.fields ?? []) - .filter((field) => field.type !== 'RELATION') .map((field) => mapFieldMetadataToGraphQLQuery(field, maxDepthForRelations - 1), ) @@ -90,7 +88,6 @@ export const useMapFieldMetadataToGraphQLQuery = () => { node { id ${(relationMetadataItem?.fields ?? []) - .filter((field) => field.type !== 'RELATION') .map((field) => mapFieldMetadataToGraphQLQuery(field, maxDepthForRelations - 1), ) diff --git a/packages/twenty-front/src/modules/object-record/hooks/useFindManyRecords.ts b/packages/twenty-front/src/modules/object-record/hooks/useFindManyRecords.ts index 323028f11..3f78365e8 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useFindManyRecords.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useFindManyRecords.ts @@ -32,11 +32,13 @@ export const useFindManyRecords = ({ onCompleted, skip, useRecordsWithoutConnection = false, + depth, }: ObjectMetadataItemIdentifier & ObjectRecordQueryVariables & { onCompleted?: (data: ObjectRecordConnection) => void; skip?: boolean; useRecordsWithoutConnection?: boolean; + depth?: number; }) => { const findManyQueryStateIdentifier = objectNameSingular + @@ -56,9 +58,12 @@ export const useFindManyRecords = ({ isFetchingMoreRecordsFamilyState(findManyQueryStateIdentifier), ); - const { objectMetadataItem, findManyRecordsQuery } = useObjectMetadataItem({ - objectNameSingular, - }); + const { objectMetadataItem, findManyRecordsQuery } = useObjectMetadataItem( + { + objectNameSingular, + }, + depth, + ); const { enqueueSnackBar } = useSnackBar(); const currentWorkspace = useRecoilValue(currentWorkspaceState);