Skip to content

Commit

Permalink
Adding message thread component
Browse files Browse the repository at this point in the history
  • Loading branch information
Thomas Trompette committed Jan 23, 2024
1 parent 3d6f1f1 commit 8276df3
Show file tree
Hide file tree
Showing 7 changed files with 258 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React from 'react';
import styled from '@emotion/styled';
import { useRecoilState } from 'recoil';

import { MessageThreadBody } from '@/activities/emails/components/MessageThreadBody';
import { MessageThreadBodyPreview } from '@/activities/emails/components/MessageThreadBodyPreview';
import { MessageThreadSender } from '@/activities/emails/components/MessageThreadSender';
import { EmailUser } from '@/activities/emails/right-drawer/components/RightDrawerThread';
import { viewableMessageThreadIdsFamilyState } from '@/activities/emails/state/viewableMessageThreadIdsFamilyState';

const StyledMessageThread = styled.div`
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
cursor: pointer;
display: flex;
flex-direction: column;
padding: 16px 24px;
`;

const StyledMessageThreadHeader = styled.div`
display: flex;
flex-direction: column;
padding-bottom: 8px;
justify-content: space-between;
`;

type MessageThreadProps = {
id: string;
body: string;
sentAt: string;
from: EmailUser;
to: EmailUser[];
};

export const MessageThread = ({
id,
body,
sentAt,
from,
}: MessageThreadProps) => {
const { displayName, avatarUrl } = from;
const [openedMessageId, setIsOpenedMessage] = useRecoilState(
viewableMessageThreadIdsFamilyState(id),
);

const isOpenedMessage = openedMessageId === id;

const updateIsOpenedMessage = () => {
if (isOpenedMessage) {
setIsOpenedMessage(null);
} else {
setIsOpenedMessage(id);
}
};

return (
<StyledMessageThread onClick={() => updateIsOpenedMessage()}>
<StyledMessageThreadHeader>
<MessageThreadSender
displayName={displayName}
avatarUrl={avatarUrl}
sentAt={sentAt}
/>
</StyledMessageThreadHeader>
{isOpenedMessage ? (
<MessageThreadBody body={body} />
) : (
<MessageThreadBodyPreview body={body} />
)}
</StyledMessageThread>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react';
import styled from '@emotion/styled';

const StyledMessageThreadBody = styled.div`
color: ${({ theme }) => theme.font.color.primary};
display: flex;
flex-direction: column;
margin-top: ${({ theme }) => theme.spacing(4)};
white-space: pre-line;
`;

type MessageThreadBodyProps = {
body: string;
};

export const MessageThreadBody = ({ body }: MessageThreadBodyProps) => {
return <StyledMessageThreadBody>{body}</StyledMessageThreadBody>;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';
import styled from '@emotion/styled';

const StyledMessageThreadBodyPreview = styled.span`
color: ${({ theme }) => theme.font.color.tertiary};
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: ${({ theme }) => theme.font.size.sm};
`;

type MessageThreadBodyPreviewProps = {
body: string;
};

export const MessageThreadBodyPreview = ({
body,
}: MessageThreadBodyPreviewProps) => {
return (
<StyledMessageThreadBodyPreview>{body}</StyledMessageThreadBodyPreview>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React from 'react';
import styled from '@emotion/styled';

import { Avatar } from '@/users/components/Avatar';
import { beautifyPastDateRelativeToNow } from '~/utils/date-utils';

const StyledMessageThreadSender = styled.div`
display: flex;
justify-content: space-between;
`;

const StyledMessageThreadSenderUser = styled.div`
align-items: flex-start;
display: flex;
`;

const StyledAvatar = styled(Avatar)`
margin: ${({ theme }) => theme.spacing(0, 1)};
`;

const StyledSenderName = styled.span`
font-size: ${({ theme }) => theme.font.size.sm};
overflow: hidden;
text-overflow: ellipsis;
`;

const StyledMessageThreadSentAt = styled.div`
align-items: flex-end;
display: flex;
color: ${({ theme }) => theme.font.color.tertiary};
font-size: ${({ theme }) => theme.font.size.sm};
`;

type MessageThreadSenderProps = {
displayName: string;
avatarUrl: string;
sentAt: string;
};

export const MessageThreadSender = ({
displayName,
avatarUrl,
sentAt,
}: MessageThreadSenderProps) => {
return (
<StyledMessageThreadSender>
<StyledMessageThreadSenderUser>
<StyledAvatar
avatarUrl={avatarUrl}
type="rounded"
placeholder={displayName}
size="sm"
/>
<StyledSenderName>{displayName}</StyledSenderName>
</StyledMessageThreadSenderUser>
<StyledMessageThreadSentAt>
{beautifyPastDateRelativeToNow(sentAt)}
</StyledMessageThreadSentAt>
</StyledMessageThreadSender>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,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';
import { Scalars, TimelineThread } from '~/generated/graphql';

const StyledContainer = styled.div`
display: flex;
Expand Down Expand Up @@ -48,13 +48,31 @@ export const Threads = ({ entity }: { entity: ActivityTargetableObject }) => {
return;
}

const timelineThreads: TimelineThread[] =
const fetchedTimelineThreads: TimelineThread[] =
threads.data[
entity.targetObjectNameSingular === 'Person'
? 'getTimelineThreadsFromPersonId'
: 'getTimelineThreadsFromCompanyId'
];

const testTimelineThreads: TimelineThread[] = [
{
__typename: 'TimelineThread',
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:
'https://favicon.twenty.com/qonto.com' as Scalars['String'],
subject: 'Test email' as Scalars['String'],
},
];

const timelineThreads =
fetchedTimelineThreads.length > 0
? fetchedTimelineThreads
: testTimelineThreads;
return (
<StyledContainer>
<Section>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,84 @@
import React from 'react';
import styled from '@emotion/styled';
import { DateTime } from 'luxon';

import { MessageThread } from '@/activities/emails/components/MessageThread';
import { ThreadHeader } from '@/activities/emails/components/ThreadHeader';

const StyledContainer = styled.div`
box-sizing: border-box;
display: flex;
flex-direction: column;
height: 100%;
justify-content: space-between;
justify-content: flex-start;
overflow-y: auto;
position: relative;
`;

export type EmailUser = {
avatarUrl: string;
displayName: string;
workspaceMemberId?: string;
personId?: string;
};

export type Message = {
id: string;
from: EmailUser;
to: EmailUser[];
subject: string;
body: string;
sentAt: string;
};

export const RightDrawerThread = () => {
const mockedThread = {
subject: 'Tes with long subject, very long subject, very long subject',
receivedAt: new Date(),
};

const mockedMessages: Message[] = 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() ?? '',
}));

return (
<StyledContainer>
<ThreadHeader
subject={mockedThread.subject}
lastMessageSentAt={mockedThread.receivedAt}
/>
{mockedMessages.map((message) => (
<MessageThread
key={message.id}
id={message.id}
from={message.from}
to={message.to}
body={message.body}
sentAt={message.sentAt}
/>
))}
</StyledContainer>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { atomFamily } from 'recoil';

export const viewableMessageThreadIdsFamilyState = atomFamily<
string | null,
string
>({
key: 'viewableMessageThreadIdsState',
default: null,
});

0 comments on commit 8276df3

Please sign in to comment.