3263 modify timeline messagingservice to allow the frontend to get multiple participants in a thread (#3611)
* wip * wip * add pagination * wip * wip * wip * update resolver * wip * wip * endpoint is working but there is still work to do * merge main * wip * subject is now first subject * number of messages is working * improving query * fix bug * fix bug * added parameter * pagination introduced a bug * pagination is working * fix type * improve typing * improve typing * fix bug * add displayName * display displayName in the frontend * move entities * fix * generate metadata * add avatarUrl * modify after comments on PR * updates * remove email mocks * remove console log * move files * remove mock * use constant * use constant * use fragments * remove console.log * generate * changes made * update DTO * generate
This commit is contained in:
@ -443,6 +443,17 @@ export type Telemetry = {
|
||||
enabled: Scalars['Boolean']['output'];
|
||||
};
|
||||
|
||||
export type TimelineThreadParticipant = {
|
||||
__typename?: 'TimelineThreadParticipant';
|
||||
avatarUrl: Scalars['String']['output'];
|
||||
displayName: Scalars['String']['output'];
|
||||
firstName: Scalars['String']['output'];
|
||||
handle: Scalars['String']['output'];
|
||||
lastName: Scalars['String']['output'];
|
||||
personId?: Maybe<Scalars['String']['output']>;
|
||||
workspaceMemberId?: Maybe<Scalars['String']['output']>;
|
||||
};
|
||||
|
||||
export type UpdateFieldInput = {
|
||||
defaultValue?: InputMaybe<Scalars['JSON']['input']>;
|
||||
description?: InputMaybe<Scalars['String']['input']>;
|
||||
|
||||
@ -402,12 +402,16 @@ export type QueryFindWorkspaceFromInviteHashArgs = {
|
||||
|
||||
|
||||
export type QueryGetTimelineThreadsFromCompanyIdArgs = {
|
||||
companyId: Scalars['String'];
|
||||
companyId: Scalars['ID'];
|
||||
page: Scalars['Int'];
|
||||
pageSize: Scalars['Int'];
|
||||
};
|
||||
|
||||
|
||||
export type QueryGetTimelineThreadsFromPersonIdArgs = {
|
||||
personId: Scalars['String'];
|
||||
page: Scalars['Int'];
|
||||
pageSize: Scalars['Int'];
|
||||
personId: Scalars['ID'];
|
||||
};
|
||||
|
||||
|
||||
@ -491,15 +495,28 @@ export type Telemetry = {
|
||||
|
||||
export type TimelineThread = {
|
||||
__typename?: 'TimelineThread';
|
||||
body: Scalars['String'];
|
||||
firstParticipant: TimelineThreadParticipant;
|
||||
id: Scalars['ID'];
|
||||
lastMessageBody: Scalars['String'];
|
||||
lastMessageReceivedAt: Scalars['DateTime'];
|
||||
lastTwoParticipants: Array<TimelineThreadParticipant>;
|
||||
numberOfMessagesInThread: Scalars['Float'];
|
||||
participantCount: Scalars['Float'];
|
||||
read: Scalars['Boolean'];
|
||||
receivedAt: Scalars['DateTime'];
|
||||
senderName: Scalars['String'];
|
||||
senderPictureUrl: Scalars['String'];
|
||||
subject: Scalars['String'];
|
||||
};
|
||||
|
||||
export type TimelineThreadParticipant = {
|
||||
__typename?: 'TimelineThreadParticipant';
|
||||
avatarUrl: Scalars['String'];
|
||||
displayName: Scalars['String'];
|
||||
firstName: Scalars['String'];
|
||||
handle: Scalars['String'];
|
||||
lastName: Scalars['String'];
|
||||
personId?: Maybe<Scalars['ID']>;
|
||||
workspaceMemberId?: Maybe<Scalars['ID']>;
|
||||
};
|
||||
|
||||
export type TransientToken = {
|
||||
__typename?: 'TransientToken';
|
||||
transientToken: AuthToken;
|
||||
@ -694,19 +711,27 @@ export type RelationEdge = {
|
||||
node: Relation;
|
||||
};
|
||||
|
||||
export type ParticipantFragmentFragment = { __typename?: 'TimelineThreadParticipant', personId?: string | null, workspaceMemberId?: string | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string };
|
||||
|
||||
export type TimelineThreadFragmentFragment = { __typename?: 'TimelineThread', id: string, read: boolean, lastMessageReceivedAt: string, lastMessageBody: string, subject: string, numberOfMessagesInThread: number, participantCount: number, firstParticipant: { __typename?: 'TimelineThreadParticipant', personId?: string | null, workspaceMemberId?: string | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }, lastTwoParticipants: Array<{ __typename?: 'TimelineThreadParticipant', personId?: string | null, workspaceMemberId?: string | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }> };
|
||||
|
||||
export type GetTimelineThreadsFromCompanyIdQueryVariables = Exact<{
|
||||
companyId: Scalars['String'];
|
||||
companyId: Scalars['ID'];
|
||||
page: Scalars['Int'];
|
||||
pageSize: Scalars['Int'];
|
||||
}>;
|
||||
|
||||
|
||||
export type GetTimelineThreadsFromCompanyIdQuery = { __typename?: 'Query', getTimelineThreadsFromCompanyId: Array<{ __typename?: 'TimelineThread', body: string, numberOfMessagesInThread: number, read: boolean, receivedAt: string, senderName: string, senderPictureUrl: string, subject: string }> };
|
||||
export type GetTimelineThreadsFromCompanyIdQuery = { __typename?: 'Query', getTimelineThreadsFromCompanyId: Array<{ __typename?: 'TimelineThread', id: string, read: boolean, lastMessageReceivedAt: string, lastMessageBody: string, subject: string, numberOfMessagesInThread: number, participantCount: number, firstParticipant: { __typename?: 'TimelineThreadParticipant', personId?: string | null, workspaceMemberId?: string | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }, lastTwoParticipants: Array<{ __typename?: 'TimelineThreadParticipant', personId?: string | null, workspaceMemberId?: string | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }> }> };
|
||||
|
||||
export type GetTimelineThreadsFromPersonIdQueryVariables = Exact<{
|
||||
personId: Scalars['String'];
|
||||
personId: Scalars['ID'];
|
||||
page: Scalars['Int'];
|
||||
pageSize: Scalars['Int'];
|
||||
}>;
|
||||
|
||||
|
||||
export type GetTimelineThreadsFromPersonIdQuery = { __typename?: 'Query', getTimelineThreadsFromPersonId: Array<{ __typename?: 'TimelineThread', body: string, numberOfMessagesInThread: number, read: boolean, receivedAt: string, senderName: string, senderPictureUrl: string, subject: string }> };
|
||||
export type GetTimelineThreadsFromPersonIdQuery = { __typename?: 'Query', getTimelineThreadsFromPersonId: Array<{ __typename?: 'TimelineThread', id: string, read: boolean, lastMessageReceivedAt: string, lastMessageBody: string, subject: string, numberOfMessagesInThread: number, participantCount: number, firstParticipant: { __typename?: 'TimelineThreadParticipant', personId?: string | null, workspaceMemberId?: string | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }, lastTwoParticipants: Array<{ __typename?: 'TimelineThreadParticipant', personId?: string | null, workspaceMemberId?: string | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }> }> };
|
||||
|
||||
export type CreateEventMutationVariables = Exact<{
|
||||
type: Scalars['String'];
|
||||
@ -859,6 +884,34 @@ export type GetWorkspaceFromInviteHashQueryVariables = Exact<{
|
||||
|
||||
export type GetWorkspaceFromInviteHashQuery = { __typename?: 'Query', findWorkspaceFromInviteHash: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, allowImpersonation: boolean } };
|
||||
|
||||
export const ParticipantFragmentFragmentDoc = gql`
|
||||
fragment ParticipantFragment on TimelineThreadParticipant {
|
||||
personId
|
||||
workspaceMemberId
|
||||
firstName
|
||||
lastName
|
||||
displayName
|
||||
avatarUrl
|
||||
handle
|
||||
}
|
||||
`;
|
||||
export const TimelineThreadFragmentFragmentDoc = gql`
|
||||
fragment TimelineThreadFragment on TimelineThread {
|
||||
id
|
||||
read
|
||||
firstParticipant {
|
||||
...ParticipantFragment
|
||||
}
|
||||
lastTwoParticipants {
|
||||
...ParticipantFragment
|
||||
}
|
||||
lastMessageReceivedAt
|
||||
lastMessageBody
|
||||
subject
|
||||
numberOfMessagesInThread
|
||||
participantCount
|
||||
}
|
||||
${ParticipantFragmentFragmentDoc}`;
|
||||
export const AuthTokenFragmentFragmentDoc = gql`
|
||||
fragment AuthTokenFragment on AuthToken {
|
||||
token
|
||||
@ -911,18 +964,16 @@ export const UserQueryFragmentFragmentDoc = gql`
|
||||
}
|
||||
`;
|
||||
export const GetTimelineThreadsFromCompanyIdDocument = gql`
|
||||
query GetTimelineThreadsFromCompanyId($companyId: String!) {
|
||||
getTimelineThreadsFromCompanyId(companyId: $companyId) {
|
||||
body
|
||||
numberOfMessagesInThread
|
||||
read
|
||||
receivedAt
|
||||
senderName
|
||||
senderPictureUrl
|
||||
subject
|
||||
query GetTimelineThreadsFromCompanyId($companyId: ID!, $page: Int!, $pageSize: Int!) {
|
||||
getTimelineThreadsFromCompanyId(
|
||||
companyId: $companyId
|
||||
page: $page
|
||||
pageSize: $pageSize
|
||||
) {
|
||||
...TimelineThreadFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
${TimelineThreadFragmentFragmentDoc}`;
|
||||
|
||||
/**
|
||||
* __useGetTimelineThreadsFromCompanyIdQuery__
|
||||
@ -937,6 +988,8 @@ export const GetTimelineThreadsFromCompanyIdDocument = gql`
|
||||
* const { data, loading, error } = useGetTimelineThreadsFromCompanyIdQuery({
|
||||
* variables: {
|
||||
* companyId: // value for 'companyId'
|
||||
* page: // value for 'page'
|
||||
* pageSize: // value for 'pageSize'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
@ -952,18 +1005,16 @@ export type GetTimelineThreadsFromCompanyIdQueryHookResult = ReturnType<typeof u
|
||||
export type GetTimelineThreadsFromCompanyIdLazyQueryHookResult = ReturnType<typeof useGetTimelineThreadsFromCompanyIdLazyQuery>;
|
||||
export type GetTimelineThreadsFromCompanyIdQueryResult = Apollo.QueryResult<GetTimelineThreadsFromCompanyIdQuery, GetTimelineThreadsFromCompanyIdQueryVariables>;
|
||||
export const GetTimelineThreadsFromPersonIdDocument = gql`
|
||||
query GetTimelineThreadsFromPersonId($personId: String!) {
|
||||
getTimelineThreadsFromPersonId(personId: $personId) {
|
||||
body
|
||||
numberOfMessagesInThread
|
||||
read
|
||||
receivedAt
|
||||
senderName
|
||||
senderPictureUrl
|
||||
subject
|
||||
query GetTimelineThreadsFromPersonId($personId: ID!, $page: Int!, $pageSize: Int!) {
|
||||
getTimelineThreadsFromPersonId(
|
||||
personId: $personId
|
||||
page: $page
|
||||
pageSize: $pageSize
|
||||
) {
|
||||
...TimelineThreadFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
${TimelineThreadFragmentFragmentDoc}`;
|
||||
|
||||
/**
|
||||
* __useGetTimelineThreadsFromPersonIdQuery__
|
||||
@ -978,6 +1029,8 @@ export const GetTimelineThreadsFromPersonIdDocument = gql`
|
||||
* const { data, loading, error } = useGetTimelineThreadsFromPersonIdQuery({
|
||||
* variables: {
|
||||
* personId: // value for 'personId'
|
||||
* page: // value for 'page'
|
||||
* pageSize: // value for 'pageSize'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
|
||||
@ -92,20 +92,22 @@ export const EmailThreadPreview = ({
|
||||
<StyledCardContent onClick={() => onClick()} divider={divider}>
|
||||
<StyledHeading unread={!thread.read}>
|
||||
<StyledAvatar
|
||||
avatarUrl={thread.senderPictureUrl}
|
||||
placeholder={thread.senderName}
|
||||
avatarUrl={thread.firstParticipant.avatarUrl}
|
||||
placeholder={thread.firstParticipant.displayName}
|
||||
type="rounded"
|
||||
/>
|
||||
<StyledSenderName>{thread.senderName}</StyledSenderName>
|
||||
<StyledSenderName>
|
||||
{thread.firstParticipant.displayName}
|
||||
</StyledSenderName>
|
||||
<StyledThreadCount>{thread.numberOfMessagesInThread}</StyledThreadCount>
|
||||
</StyledHeading>
|
||||
|
||||
<StyledSubjectAndBody>
|
||||
<StyledSubject unread={!thread.read}>{thread.subject}</StyledSubject>
|
||||
<StyledBody>{thread.body}</StyledBody>
|
||||
<StyledBody>{thread.lastMessageBody}</StyledBody>
|
||||
</StyledSubjectAndBody>
|
||||
<StyledReceivedAt>
|
||||
{formatToHumanReadableDate(thread.receivedAt)}
|
||||
{formatToHumanReadableDate(thread.lastMessageReceivedAt)}
|
||||
</StyledReceivedAt>
|
||||
</StyledCardContent>
|
||||
);
|
||||
|
||||
@ -2,11 +2,8 @@ import { useQuery } from '@apollo/client';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { EmailThreadPreview } from '@/activities/emails/components/EmailThreadPreview';
|
||||
import { TIMELINE_THREADS_DEFAULT_PAGE_SIZE } from '@/activities/emails/constants/messaging.constants';
|
||||
import { useEmailThread } from '@/activities/emails/hooks/useEmailThread';
|
||||
import {
|
||||
mockedEmailThreads,
|
||||
MockedThread,
|
||||
} from '@/activities/emails/mocks/mockedEmailThreads';
|
||||
import { getTimelineThreadsFromCompanyId } from '@/activities/emails/queries/getTimelineThreadsFromCompanyId';
|
||||
import { getTimelineThreadsFromPersonId } from '@/activities/emails/queries/getTimelineThreadsFromPersonId';
|
||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||
@ -17,6 +14,7 @@ import {
|
||||
} from '@/ui/display/typography/components/H1Title';
|
||||
import { Card } from '@/ui/layout/card/components/Card';
|
||||
import { Section } from '@/ui/layout/section/components/Section';
|
||||
import { TimelineThread } from '~/generated/graphql';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
@ -46,10 +44,13 @@ export const EmailThreads = ({
|
||||
? getTimelineThreadsFromPersonId
|
||||
: getTimelineThreadsFromCompanyId;
|
||||
|
||||
const threadQueryVariables =
|
||||
entity.targetObjectNameSingular === CoreObjectNameSingular.Person
|
||||
const threadQueryVariables = {
|
||||
...(entity.targetObjectNameSingular === CoreObjectNameSingular.Person
|
||||
? { personId: entity.id }
|
||||
: { companyId: entity.id };
|
||||
: { companyId: entity.id }),
|
||||
page: 1,
|
||||
pageSize: TIMELINE_THREADS_DEFAULT_PAGE_SIZE,
|
||||
};
|
||||
|
||||
const threads = useQuery(threadQuery, {
|
||||
variables: threadQueryVariables,
|
||||
@ -59,16 +60,12 @@ export const EmailThreads = ({
|
||||
return;
|
||||
}
|
||||
|
||||
// To use once the id is returned by the query
|
||||
|
||||
// const fetchedTimelineThreads: TimelineThread[] =
|
||||
// threads.data[
|
||||
// entity.targetObjectNameSingular === CoreObjectNameSingular.Person
|
||||
// ? 'getTimelineThreadsFromPersonId'
|
||||
// : 'getTimelineThreadsFromCompanyId'
|
||||
// ];
|
||||
|
||||
const timelineThreads = mockedEmailThreads;
|
||||
const timelineThreads: TimelineThread[] =
|
||||
threads.data[
|
||||
entity.targetObjectNameSingular === CoreObjectNameSingular.Person
|
||||
? 'getTimelineThreadsFromPersonId'
|
||||
: 'getTimelineThreadsFromCompanyId'
|
||||
];
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
@ -77,16 +74,14 @@ export const EmailThreads = ({
|
||||
title={
|
||||
<>
|
||||
Inbox{' '}
|
||||
<StyledEmailCount>
|
||||
{timelineThreads && timelineThreads.length}
|
||||
</StyledEmailCount>
|
||||
<StyledEmailCount>{timelineThreads?.length}</StyledEmailCount>
|
||||
</>
|
||||
}
|
||||
fontColor={H1TitleFontColor.Primary}
|
||||
/>
|
||||
<Card>
|
||||
{timelineThreads &&
|
||||
timelineThreads.map((thread: MockedThread, index: number) => (
|
||||
timelineThreads.map((thread: TimelineThread, index: number) => (
|
||||
<EmailThreadPreview
|
||||
key={index}
|
||||
divider={index < timelineThreads.length - 1}
|
||||
|
||||
@ -0,0 +1 @@
|
||||
export const TIMELINE_THREADS_DEFAULT_PAGE_SIZE = 20;
|
||||
@ -1,15 +1,15 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { MockedThread } from '@/activities/emails/mocks/mockedEmailThreads';
|
||||
import { useOpenEmailThreadRightDrawer } from '@/activities/emails/right-drawer/hooks/useOpenEmailThreadRightDrawer';
|
||||
import { viewableEmailThreadState } from '@/activities/emails/state/viewableEmailThreadState';
|
||||
import { TimelineThread } from '~/generated/graphql';
|
||||
|
||||
export const useEmailThread = () => {
|
||||
const [, setViewableEmailThread] = useRecoilState(viewableEmailThreadState);
|
||||
|
||||
const openEmailThredRightDrawer = useOpenEmailThreadRightDrawer();
|
||||
|
||||
const openEmailThread = (thread: MockedThread) => {
|
||||
const openEmailThread = (thread: TimelineThread) => {
|
||||
openEmailThredRightDrawer();
|
||||
|
||||
setViewableEmailThread(thread);
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
import { Scalars, TimelineThread } from '~/generated/graphql';
|
||||
|
||||
export type MockedThread = {
|
||||
id: string;
|
||||
} & TimelineThread;
|
||||
|
||||
export const mockedEmailThreads: MockedThread[] = [
|
||||
{
|
||||
__typename: 'TimelineThread',
|
||||
id: 'ec7e12b9-4063-410f-ae9a-30e32452b9c0',
|
||||
body: 'This is a test email' as Scalars['String'],
|
||||
numberOfMessagesInThread: 5 as Scalars['Float'],
|
||||
read: true as Scalars['Boolean'],
|
||||
receivedAt: new Date().toISOString() as Scalars['DateTime'],
|
||||
senderName: 'Thom Trp' as Scalars['String'],
|
||||
senderPictureUrl: '' as Scalars['String'],
|
||||
subject: 'Test email' as Scalars['String'],
|
||||
},
|
||||
{
|
||||
__typename: 'TimelineThread',
|
||||
id: 'ec7e12b9-4063-410f-ae9a-30e32452b9c0',
|
||||
body: 'This is a second test email' as Scalars['String'],
|
||||
numberOfMessagesInThread: 5 as Scalars['Float'],
|
||||
read: true as Scalars['Boolean'],
|
||||
receivedAt: new Date().toISOString() as Scalars['DateTime'],
|
||||
senderName: 'Coco Den' as Scalars['String'],
|
||||
senderPictureUrl: '' as Scalars['String'],
|
||||
subject: 'Test email number 2' as Scalars['String'],
|
||||
},
|
||||
];
|
||||
@ -0,0 +1,13 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const participantFragment = gql`
|
||||
fragment ParticipantFragment on TimelineThreadParticipant {
|
||||
personId
|
||||
workspaceMemberId
|
||||
firstName
|
||||
lastName
|
||||
displayName
|
||||
avatarUrl
|
||||
handle
|
||||
}
|
||||
`;
|
||||
@ -0,0 +1,22 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { participantFragment } from '@/activities/emails/queries/fragments/participantFragment';
|
||||
|
||||
export const timelineThreadFragment = gql`
|
||||
fragment TimelineThreadFragment on TimelineThread {
|
||||
id
|
||||
read
|
||||
firstParticipant {
|
||||
...ParticipantFragment
|
||||
}
|
||||
lastTwoParticipants {
|
||||
...ParticipantFragment
|
||||
}
|
||||
lastMessageReceivedAt
|
||||
lastMessageBody
|
||||
subject
|
||||
numberOfMessagesInThread
|
||||
participantCount
|
||||
}
|
||||
${participantFragment}
|
||||
`;
|
||||
@ -1,15 +1,20 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { timelineThreadFragment } from '@/activities/emails/queries/fragments/timelineThreadFragment';
|
||||
|
||||
export const getTimelineThreadsFromCompanyId = gql`
|
||||
query GetTimelineThreadsFromCompanyId($companyId: String!) {
|
||||
getTimelineThreadsFromCompanyId(companyId: $companyId) {
|
||||
body
|
||||
numberOfMessagesInThread
|
||||
read
|
||||
receivedAt
|
||||
senderName
|
||||
senderPictureUrl
|
||||
subject
|
||||
query GetTimelineThreadsFromCompanyId(
|
||||
$companyId: ID!
|
||||
$page: Int!
|
||||
$pageSize: Int!
|
||||
) {
|
||||
getTimelineThreadsFromCompanyId(
|
||||
companyId: $companyId
|
||||
page: $page
|
||||
pageSize: $pageSize
|
||||
) {
|
||||
...TimelineThreadFragment
|
||||
}
|
||||
}
|
||||
${timelineThreadFragment}
|
||||
`;
|
||||
|
||||
@ -1,15 +1,20 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { timelineThreadFragment } from '@/activities/emails/queries/fragments/timelineThreadFragment';
|
||||
|
||||
export const getTimelineThreadsFromPersonId = gql`
|
||||
query GetTimelineThreadsFromPersonId($personId: String!) {
|
||||
getTimelineThreadsFromPersonId(personId: $personId) {
|
||||
body
|
||||
numberOfMessagesInThread
|
||||
read
|
||||
receivedAt
|
||||
senderName
|
||||
senderPictureUrl
|
||||
subject
|
||||
query GetTimelineThreadsFromPersonId(
|
||||
$personId: ID!
|
||||
$page: Int!
|
||||
$pageSize: Int!
|
||||
) {
|
||||
getTimelineThreadsFromPersonId(
|
||||
personId: $personId
|
||||
page: $page
|
||||
pageSize: $pageSize
|
||||
) {
|
||||
...TimelineThreadFragment
|
||||
}
|
||||
}
|
||||
${timelineThreadFragment}
|
||||
`;
|
||||
|
||||
@ -50,7 +50,7 @@ export const RightDrawerEmailThread = () => {
|
||||
<StyledContainer>
|
||||
<EmailThreadHeader
|
||||
subject={viewableEmailThread.subject}
|
||||
lastMessageSentAt={viewableEmailThread.receivedAt}
|
||||
lastMessageSentAt={viewableEmailThread.lastMessageReceivedAt}
|
||||
/>
|
||||
{messages.map((message) => (
|
||||
<EmailThreadMessage
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
import { MockedThread } from '@/activities/emails/mocks/mockedEmailThreads';
|
||||
import { TimelineThread } from '~/generated/graphql';
|
||||
|
||||
export const viewableEmailThreadState = atom<MockedThread | null>({
|
||||
export const viewableEmailThreadState = atom<TimelineThread | null>({
|
||||
key: 'viewableEmailThreadState',
|
||||
default: null,
|
||||
});
|
||||
|
||||
@ -4,7 +4,6 @@ import { Comment } from '@/activities/types/Comment';
|
||||
import { Company } from '@/companies/types/Company';
|
||||
import { Person } from '@/people/types/Person';
|
||||
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
|
||||
import { TimelineThread } from '~/generated/graphql';
|
||||
|
||||
type MockedActivity = Pick<
|
||||
Activity,
|
||||
@ -211,24 +210,3 @@ export const mockedActivities: Array<MockedActivity> = [
|
||||
__typename: 'Activity',
|
||||
},
|
||||
];
|
||||
|
||||
export const mockedEmailThreads: TimelineThread[] = [
|
||||
{
|
||||
body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras dignissim nisi eu tellus dapibus, egestas placerat risus placerat. Praesent eget arcu consectetur, efficitur felis.',
|
||||
numberOfMessagesInThread: 4,
|
||||
read: false,
|
||||
receivedAt: new Date('11/04/2023').toISOString(),
|
||||
senderName: 'Steve Anahi',
|
||||
senderPictureUrl: '',
|
||||
subject: 'Partnerships',
|
||||
},
|
||||
{
|
||||
body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras dignissim nisi eu tellus dapibus, egestas placerat risus placerat. Praesent eget arcu consectetur, efficitur felis.',
|
||||
numberOfMessagesInThread: 3,
|
||||
read: true,
|
||||
receivedAt: new Date('11/04/2023').toISOString(),
|
||||
senderName: 'Alexandre Prot',
|
||||
senderPictureUrl: '',
|
||||
subject: 'Next step',
|
||||
},
|
||||
];
|
||||
|
||||
@ -0,0 +1,2 @@
|
||||
export const TIMELINE_THREADS_DEFAULT_PAGE_SIZE = 20;
|
||||
export const TIMELINE_THREADS_MAX_PAGE_SIZE = 50;
|
||||
@ -0,0 +1,25 @@
|
||||
import { ObjectType, Field, ID } from '@nestjs/graphql';
|
||||
|
||||
@ObjectType('TimelineThreadParticipant')
|
||||
export class TimelineThreadParticipant {
|
||||
@Field(() => ID, { nullable: true })
|
||||
personId: string;
|
||||
|
||||
@Field(() => ID, { nullable: true })
|
||||
workspaceMemberId: string;
|
||||
|
||||
@Field()
|
||||
firstName: string;
|
||||
|
||||
@Field()
|
||||
lastName: string;
|
||||
|
||||
@Field()
|
||||
displayName: string;
|
||||
|
||||
@Field()
|
||||
avatarUrl: string;
|
||||
|
||||
@Field()
|
||||
handle: string;
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
import { ObjectType, Field, ID } from '@nestjs/graphql';
|
||||
|
||||
import { IDField } from '@ptc-org/nestjs-query-graphql';
|
||||
|
||||
import { TimelineThreadParticipant } from 'src/core/messaging/dtos/timeline-thread-participant.dto';
|
||||
|
||||
@ObjectType('TimelineThread')
|
||||
export class TimelineThread {
|
||||
@IDField(() => ID)
|
||||
id: string;
|
||||
|
||||
@Field()
|
||||
read: boolean;
|
||||
|
||||
@Field()
|
||||
firstParticipant: TimelineThreadParticipant;
|
||||
|
||||
@Field(() => [TimelineThreadParticipant])
|
||||
lastTwoParticipants: TimelineThreadParticipant[];
|
||||
|
||||
@Field()
|
||||
lastMessageReceivedAt: Date;
|
||||
|
||||
@Field()
|
||||
lastMessageBody: string;
|
||||
|
||||
@Field()
|
||||
subject: string;
|
||||
|
||||
@Field()
|
||||
numberOfMessagesInThread: number;
|
||||
|
||||
@Field()
|
||||
participantCount: number;
|
||||
}
|
||||
@ -1,43 +1,47 @@
|
||||
import { Args, Query, Field, Resolver, ObjectType } from '@nestjs/graphql';
|
||||
import {
|
||||
Args,
|
||||
Query,
|
||||
Resolver,
|
||||
Int,
|
||||
ArgsType,
|
||||
Field,
|
||||
ID,
|
||||
} from '@nestjs/graphql';
|
||||
import { UseGuards } from '@nestjs/common';
|
||||
|
||||
import { Column, Entity } from 'typeorm';
|
||||
import { Max } from 'class-validator';
|
||||
|
||||
import { JwtAuthGuard } from 'src/guards/jwt.auth.guard';
|
||||
import { Workspace } from 'src/core/workspace/workspace.entity';
|
||||
import { AuthWorkspace } from 'src/decorators/auth-workspace.decorator';
|
||||
import { TimelineMessagingService } from 'src/core/messaging/timeline-messaging.service';
|
||||
import { TimelineThread } from 'src/core/messaging/dtos/timeline-thread.dto';
|
||||
import { TIMELINE_THREADS_MAX_PAGE_SIZE } from 'src/core/messaging/constants/messaging.constants';
|
||||
|
||||
@Entity({ name: 'timelineThread', schema: 'core' })
|
||||
@ObjectType('TimelineThread')
|
||||
export class TimelineThread {
|
||||
@Field()
|
||||
@Column()
|
||||
read: boolean;
|
||||
@ArgsType()
|
||||
class GetTimelineThreadsFromPersonIdArgs {
|
||||
@Field(() => ID)
|
||||
personId: string;
|
||||
|
||||
@Field()
|
||||
@Column()
|
||||
senderName: string;
|
||||
@Field(() => Int)
|
||||
page: number;
|
||||
|
||||
@Field()
|
||||
@Column()
|
||||
senderPictureUrl: string;
|
||||
@Field(() => Int)
|
||||
@Max(TIMELINE_THREADS_MAX_PAGE_SIZE)
|
||||
pageSize: number;
|
||||
}
|
||||
|
||||
@Field()
|
||||
@Column()
|
||||
numberOfMessagesInThread: number;
|
||||
@ArgsType()
|
||||
class GetTimelineThreadsFromCompanyIdArgs {
|
||||
@Field(() => ID)
|
||||
companyId: string;
|
||||
|
||||
@Field()
|
||||
@Column()
|
||||
subject: string;
|
||||
@Field(() => Int)
|
||||
page: number;
|
||||
|
||||
@Field()
|
||||
@Column()
|
||||
body: string;
|
||||
|
||||
@Field()
|
||||
@Column()
|
||||
receivedAt: Date;
|
||||
@Field(() => Int)
|
||||
@Max(TIMELINE_THREADS_MAX_PAGE_SIZE)
|
||||
pageSize: number;
|
||||
}
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@ -50,12 +54,14 @@ export class TimelineMessagingResolver {
|
||||
@Query(() => [TimelineThread])
|
||||
async getTimelineThreadsFromPersonId(
|
||||
@AuthWorkspace() { id: workspaceId }: Workspace,
|
||||
@Args('personId') personId: string,
|
||||
@Args() { personId, page, pageSize }: GetTimelineThreadsFromPersonIdArgs,
|
||||
) {
|
||||
const timelineThreads =
|
||||
await this.timelineMessagingService.getMessagesFromPersonIds(
|
||||
workspaceId,
|
||||
[personId],
|
||||
page,
|
||||
pageSize,
|
||||
);
|
||||
|
||||
return timelineThreads;
|
||||
@ -64,12 +70,14 @@ export class TimelineMessagingResolver {
|
||||
@Query(() => [TimelineThread])
|
||||
async getTimelineThreadsFromCompanyId(
|
||||
@AuthWorkspace() { id: workspaceId }: Workspace,
|
||||
@Args('companyId') companyId: string,
|
||||
@Args() { companyId, page, pageSize }: GetTimelineThreadsFromCompanyIdArgs,
|
||||
) {
|
||||
const timelineThreads =
|
||||
await this.timelineMessagingService.getMessagesFromCompanyId(
|
||||
workspaceId,
|
||||
companyId,
|
||||
page,
|
||||
pageSize,
|
||||
);
|
||||
|
||||
return timelineThreads;
|
||||
|
||||
@ -1,9 +1,20 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { TimelineThread } from 'src/core/messaging/timeline-messaging.resolver';
|
||||
import { TIMELINE_THREADS_DEFAULT_PAGE_SIZE } from 'src/core/messaging/constants/messaging.constants';
|
||||
import { TimelineThread } from 'src/core/messaging/dtos/timeline-thread.dto';
|
||||
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
||||
import { DataSourceService } from 'src/metadata/data-source/data-source.service';
|
||||
|
||||
type TimelineThreadParticipant = {
|
||||
personId: string;
|
||||
workspaceMemberId: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
displayName: string;
|
||||
avatarUrl: string;
|
||||
handle: string;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class TimelineMessagingService {
|
||||
constructor(
|
||||
@ -14,7 +25,11 @@ export class TimelineMessagingService {
|
||||
async getMessagesFromPersonIds(
|
||||
workspaceId: string,
|
||||
personIds: string[],
|
||||
page: number = 1,
|
||||
pageSize: number = TIMELINE_THREADS_DEFAULT_PAGE_SIZE,
|
||||
): Promise<TimelineThread[]> {
|
||||
const offset = (page - 1) * TIMELINE_THREADS_DEFAULT_PAGE_SIZE;
|
||||
|
||||
const dataSourceMetadata =
|
||||
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
|
||||
workspaceId,
|
||||
@ -23,61 +38,327 @@ export class TimelineMessagingService {
|
||||
const workspaceDataSource =
|
||||
await this.typeORMService.connectToDataSource(dataSourceMetadata);
|
||||
|
||||
// 10 first threads This hard limit is just for the POC, we will implement pagination later
|
||||
const messageThreads = await workspaceDataSource?.query(
|
||||
const messageThreads:
|
||||
| {
|
||||
id: string;
|
||||
lastMessageReceivedAt: Date;
|
||||
lastMessageId: string;
|
||||
lastMessageBody: string;
|
||||
rowNumber: number;
|
||||
}[]
|
||||
| undefined = await workspaceDataSource?.query(
|
||||
`
|
||||
SELECT
|
||||
subquery.*,
|
||||
message_count,
|
||||
last_message_subject,
|
||||
last_message_text,
|
||||
last_message_received_at,
|
||||
last_message_participant_handle,
|
||||
last_message_participant_displayName
|
||||
FROM (
|
||||
SELECT
|
||||
mt.*,
|
||||
COUNT(m."id") OVER (PARTITION BY mt."id") AS message_count,
|
||||
FIRST_VALUE(m."subject") OVER (PARTITION BY mt."id" ORDER BY m."receivedAt" DESC) AS last_message_subject,
|
||||
FIRST_VALUE(m."text") OVER (PARTITION BY mt."id" ORDER BY m."receivedAt" DESC) AS last_message_text,
|
||||
FIRST_VALUE(m."receivedAt") OVER (PARTITION BY mt."id" ORDER BY m."receivedAt" DESC) AS last_message_received_at,
|
||||
FIRST_VALUE(mr."handle") OVER (PARTITION BY mt."id" ORDER BY m."receivedAt" DESC) AS last_message_participant_handle,
|
||||
FIRST_VALUE(mr."displayName") OVER (PARTITION BY mt."id" ORDER BY m."receivedAt" DESC) AS last_message_participant_displayName,
|
||||
ROW_NUMBER() OVER (PARTITION BY mt."id" ORDER BY m."receivedAt" DESC) AS rn
|
||||
FROM
|
||||
${dataSourceMetadata.schema}."messageThread" mt
|
||||
LEFT JOIN
|
||||
${dataSourceMetadata.schema}."message" m ON mt."id" = m."messageThreadId"
|
||||
LEFT JOIN
|
||||
${dataSourceMetadata.schema}."messageParticipant" mr ON m."id" = mr."messageId"
|
||||
WHERE
|
||||
mr."personId" IN (SELECT unnest($1::uuid[]))
|
||||
) AS subquery
|
||||
WHERE
|
||||
subquery.rn = 1
|
||||
ORDER BY
|
||||
subquery.last_message_received_at DESC
|
||||
LIMIT 10;
|
||||
`,
|
||||
[personIds],
|
||||
SELECT *
|
||||
FROM
|
||||
(SELECT "messageThread".id,
|
||||
MAX(message."receivedAt") AS "lastMessageReceivedAt",
|
||||
message.id AS "lastMessageId",
|
||||
message.text AS "lastMessageBody",
|
||||
ROW_NUMBER() OVER (PARTITION BY "messageThread".id ORDER BY MAX(message."receivedAt") DESC) AS "rowNumber"
|
||||
FROM
|
||||
${dataSourceMetadata.schema}."message" message
|
||||
LEFT JOIN
|
||||
${dataSourceMetadata.schema}."messageThread" "messageThread" ON "messageThread".id = message."messageThreadId"
|
||||
LEFT JOIN
|
||||
${dataSourceMetadata.schema}."messageParticipant" "messageParticipant" ON "messageParticipant"."messageId" = message.id
|
||||
LEFT JOIN
|
||||
${dataSourceMetadata.schema}."person" person ON person.id = "messageParticipant"."personId"
|
||||
LEFT JOIN
|
||||
${dataSourceMetadata.schema}."workspaceMember" "workspaceMember" ON "workspaceMember".id = "messageParticipant"."workspaceMemberId"
|
||||
WHERE
|
||||
person.id = ANY($1)
|
||||
GROUP BY
|
||||
"messageThread".id,
|
||||
message.id
|
||||
ORDER BY
|
||||
message."receivedAt" DESC
|
||||
) AS "messageThreads"
|
||||
WHERE
|
||||
"rowNumber" = 1
|
||||
LIMIT $2
|
||||
OFFSET $3
|
||||
`,
|
||||
[personIds, pageSize, offset],
|
||||
);
|
||||
|
||||
const formattedMessageThreads = messageThreads.map((messageThread) => {
|
||||
if (!messageThreads) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const messageThreadIds = messageThreads.map(
|
||||
(messageThread) => messageThread.id,
|
||||
);
|
||||
|
||||
const threadSubjects:
|
||||
| {
|
||||
id: string;
|
||||
subject: string;
|
||||
}[]
|
||||
| undefined = await workspaceDataSource?.query(
|
||||
`
|
||||
SELECT *
|
||||
FROM
|
||||
(SELECT
|
||||
"messageThread".id,
|
||||
message.subject,
|
||||
ROW_NUMBER() OVER (PARTITION BY "messageThread".id ORDER BY MAX(message."receivedAt") ASC) AS "rowNumber"
|
||||
FROM
|
||||
${dataSourceMetadata.schema}."message" message
|
||||
LEFT JOIN
|
||||
${dataSourceMetadata.schema}."messageThread" "messageThread" ON "messageThread".id = message."messageThreadId"
|
||||
WHERE
|
||||
"messageThread".id = ANY($1)
|
||||
GROUP BY
|
||||
"messageThread".id,
|
||||
message.id
|
||||
ORDER BY
|
||||
message."receivedAt" DESC
|
||||
) AS "messageThreads"
|
||||
WHERE
|
||||
"rowNumber" = 1
|
||||
`,
|
||||
[messageThreadIds],
|
||||
);
|
||||
|
||||
const numberOfMessagesInThread:
|
||||
| {
|
||||
id: string;
|
||||
numberOfMessagesInThread: number;
|
||||
}[]
|
||||
| undefined = await workspaceDataSource?.query(
|
||||
`
|
||||
SELECT
|
||||
"messageThread".id,
|
||||
COUNT(message.id) AS "numberOfMessagesInThread"
|
||||
FROM
|
||||
${dataSourceMetadata.schema}."message" message
|
||||
LEFT JOIN
|
||||
${dataSourceMetadata.schema}."messageThread" "messageThread" ON "messageThread".id = message."messageThreadId"
|
||||
WHERE
|
||||
"messageThread".id = ANY($1)
|
||||
GROUP BY
|
||||
"messageThread".id
|
||||
`,
|
||||
[messageThreadIds],
|
||||
);
|
||||
|
||||
const messageThreadsByMessageThreadId: {
|
||||
[key: string]: {
|
||||
id: string;
|
||||
lastMessageReceivedAt: Date;
|
||||
lastMessageBody: string;
|
||||
};
|
||||
} = messageThreads.reduce((messageThreadAcc, messageThread) => {
|
||||
messageThreadAcc[messageThread.id] = messageThread;
|
||||
|
||||
return messageThreadAcc;
|
||||
}, {});
|
||||
|
||||
const subjectsByMessageThreadId:
|
||||
| {
|
||||
[key: string]: {
|
||||
id: string;
|
||||
subject: string;
|
||||
};
|
||||
}
|
||||
| undefined = threadSubjects?.reduce(
|
||||
(threadSubjectAcc, threadSubject) => {
|
||||
threadSubjectAcc[threadSubject.id] = threadSubject;
|
||||
|
||||
return threadSubjectAcc;
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
const numberOfMessagesByMessageThreadId:
|
||||
| {
|
||||
[key: string]: {
|
||||
id: string;
|
||||
numberOfMessagesInThread: number;
|
||||
};
|
||||
}
|
||||
| undefined = numberOfMessagesInThread?.reduce(
|
||||
(numberOfMessagesAcc, numberOfMessages) => {
|
||||
numberOfMessagesAcc[numberOfMessages.id] = numberOfMessages;
|
||||
|
||||
return numberOfMessagesAcc;
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
const threadMessagesFromActiveParticipants:
|
||||
| {
|
||||
id: string;
|
||||
messageId: string;
|
||||
receivedAt: Date;
|
||||
body: string;
|
||||
subject: string;
|
||||
personId: string;
|
||||
workspaceMemberId: string;
|
||||
handle: string;
|
||||
personFirstName: string;
|
||||
personLastName: string;
|
||||
personAvatarUrl: string;
|
||||
workspaceMemberFirstName: string;
|
||||
workspaceMemberLastName: string;
|
||||
workspaceMemberAvatarUrl: string;
|
||||
messageDisplayName: string;
|
||||
}[]
|
||||
| undefined = await workspaceDataSource?.query(
|
||||
`
|
||||
SELECT DISTINCT "messageThread".id,
|
||||
message.id AS "messageId",
|
||||
message."receivedAt",
|
||||
message.text,
|
||||
message."subject",
|
||||
"messageParticipant"."personId",
|
||||
"messageParticipant"."workspaceMemberId",
|
||||
"messageParticipant".handle,
|
||||
"person"."nameFirstName" as "personFirstName",
|
||||
"person"."nameLastName" as "personLastName",
|
||||
"person"."avatarUrl" as "personAvatarUrl",
|
||||
"workspaceMember"."nameFirstName" as "workspaceMemberFirstName",
|
||||
"workspaceMember"."nameLastName" as "workspaceMemberLastName",
|
||||
"workspaceMember"."avatarUrl" as "workspaceMemberAvatarUrl",
|
||||
"messageParticipant"."displayName" as "messageDisplayName"
|
||||
FROM
|
||||
${dataSourceMetadata.schema}."message" message
|
||||
LEFT JOIN
|
||||
${dataSourceMetadata.schema}."messageThread" "messageThread" ON "messageThread".id = message."messageThreadId"
|
||||
LEFT JOIN
|
||||
(SELECT * FROM ${dataSourceMetadata.schema}."messageParticipant" WHERE "messageParticipant".role = 'from') "messageParticipant" ON "messageParticipant"."messageId" = message.id
|
||||
LEFT JOIN
|
||||
${dataSourceMetadata.schema}."person" person ON person."id" = "messageParticipant"."personId"
|
||||
LEFT JOIN
|
||||
${dataSourceMetadata.schema}."workspaceMember" "workspaceMember" ON "workspaceMember".id = "messageParticipant"."workspaceMemberId"
|
||||
WHERE
|
||||
"messageThread".id = ANY($1)
|
||||
ORDER BY
|
||||
message."receivedAt" DESC
|
||||
`,
|
||||
[messageThreadIds],
|
||||
);
|
||||
|
||||
const threadParticipantsByThreadId: {
|
||||
[key: string]: TimelineThreadParticipant[];
|
||||
} = messageThreadIds.reduce((messageThreadIdAcc, messageThreadId) => {
|
||||
const threadMessages = threadMessagesFromActiveParticipants?.filter(
|
||||
(threadMessage) => threadMessage.id === messageThreadId,
|
||||
);
|
||||
|
||||
const threadParticipants = threadMessages?.reduce(
|
||||
(
|
||||
threadMessageAcc,
|
||||
threadMessage,
|
||||
): {
|
||||
[key: string]: TimelineThreadParticipant;
|
||||
} => {
|
||||
const threadParticipant = threadMessageAcc[threadMessage.handle];
|
||||
|
||||
const firstName =
|
||||
threadMessage.personFirstName ||
|
||||
threadMessage.workspaceMemberFirstName ||
|
||||
'';
|
||||
|
||||
const lastName =
|
||||
threadMessage.personLastName ||
|
||||
threadMessage.workspaceMemberLastName ||
|
||||
'';
|
||||
|
||||
const displayName =
|
||||
firstName ||
|
||||
threadMessage.messageDisplayName ||
|
||||
threadMessage.handle;
|
||||
|
||||
if (!threadParticipant) {
|
||||
threadMessageAcc[threadMessage.handle] = {
|
||||
personId: threadMessage.personId,
|
||||
workspaceMemberId: threadMessage.workspaceMemberId,
|
||||
firstName,
|
||||
lastName,
|
||||
displayName,
|
||||
avatarUrl:
|
||||
threadMessage.personAvatarUrl ??
|
||||
threadMessage.workspaceMemberAvatarUrl ??
|
||||
'',
|
||||
handle: threadMessage.handle,
|
||||
};
|
||||
}
|
||||
|
||||
return threadMessageAcc;
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
messageThreadIdAcc[messageThreadId] = threadParticipants
|
||||
? Object.values(threadParticipants)
|
||||
: [];
|
||||
|
||||
return messageThreadIdAcc;
|
||||
}, {});
|
||||
|
||||
const timelineThreads = messageThreadIds.map((messageThreadId) => {
|
||||
const threadParticipants = threadParticipantsByThreadId[messageThreadId];
|
||||
|
||||
const firstParticipant = threadParticipants[0];
|
||||
|
||||
const threadParticipantsWithoutFirstParticipant =
|
||||
threadParticipants.filter(
|
||||
(threadParticipant) =>
|
||||
threadParticipant.handle !== firstParticipant.handle,
|
||||
);
|
||||
|
||||
const lastTwoParticipants: TimelineThreadParticipant[] = [];
|
||||
|
||||
const lastParticipant =
|
||||
threadParticipantsWithoutFirstParticipant.slice(-1)[0];
|
||||
|
||||
if (lastParticipant) {
|
||||
lastTwoParticipants.push(lastParticipant);
|
||||
|
||||
const threadParticipantsWithoutFirstAndLastParticipants =
|
||||
threadParticipantsWithoutFirstParticipant.filter(
|
||||
(threadParticipant) =>
|
||||
threadParticipant.handle !== lastParticipant.handle,
|
||||
);
|
||||
|
||||
if (threadParticipantsWithoutFirstAndLastParticipants.length > 0)
|
||||
lastTwoParticipants.push(
|
||||
threadParticipantsWithoutFirstAndLastParticipants.slice(-1)[0],
|
||||
);
|
||||
}
|
||||
|
||||
const thread = messageThreadsByMessageThreadId[messageThreadId];
|
||||
|
||||
const threadSubject =
|
||||
subjectsByMessageThreadId?.[messageThreadId].subject ?? '';
|
||||
|
||||
const numberOfMessages =
|
||||
numberOfMessagesByMessageThreadId?.[messageThreadId]
|
||||
.numberOfMessagesInThread ?? 1;
|
||||
|
||||
return {
|
||||
id: messageThreadId,
|
||||
read: true,
|
||||
senderName: messageThread.last_message_participant_handle,
|
||||
senderPictureUrl: '',
|
||||
numberOfMessagesInThread: messageThread.message_count,
|
||||
subject: messageThread.last_message_subject,
|
||||
body: messageThread.last_message_text,
|
||||
receivedAt: messageThread.last_message_received_at,
|
||||
firstParticipant,
|
||||
lastTwoParticipants,
|
||||
lastMessageReceivedAt: thread.lastMessageReceivedAt,
|
||||
lastMessageBody: thread.lastMessageBody,
|
||||
subject: threadSubject,
|
||||
numberOfMessagesInThread: numberOfMessages,
|
||||
participantCount: threadParticipants.length,
|
||||
};
|
||||
});
|
||||
|
||||
return formattedMessageThreads;
|
||||
return timelineThreads;
|
||||
}
|
||||
|
||||
async getMessagesFromCompanyId(workspaceId: string, companyId: string) {
|
||||
async getMessagesFromCompanyId(
|
||||
workspaceId: string,
|
||||
companyId: string,
|
||||
page: number = 1,
|
||||
pageSize: number = TIMELINE_THREADS_DEFAULT_PAGE_SIZE,
|
||||
) {
|
||||
const dataSourceMetadata =
|
||||
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
|
||||
workspaceId,
|
||||
@ -102,11 +383,15 @@ export class TimelineMessagingService {
|
||||
return [];
|
||||
}
|
||||
|
||||
const formattedPersonIds = personIds.map((personId) => personId.id);
|
||||
const formattedPersonIds = personIds.map(
|
||||
(personId: { id: string }) => personId.id,
|
||||
);
|
||||
|
||||
const messageThreads = await this.getMessagesFromPersonIds(
|
||||
workspaceId,
|
||||
formattedPersonIds,
|
||||
page,
|
||||
pageSize,
|
||||
);
|
||||
|
||||
return messageThreads;
|
||||
|
||||
Reference in New Issue
Block a user