Skip to content

Commit be26af4

Browse files
authored
Video 3732 Conversations UI - create message components (#441)
1 parent 045aee0 commit be26af4

File tree

7 files changed

+255
-4
lines changed

7 files changed

+255
-4
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
"start": "concurrently npm:server npm:dev",
6767
"dev": "react-scripts start",
6868
"build": "node ./scripts/build.js",
69-
"test": "jest",
69+
"test": "cross-env TZ=utc jest",
7070
"eject": "react-scripts eject",
7171
"lint": "eslint src",
7272
"server": "node server.js",

src/components/ChatWindow/ChatWindow.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import React from 'react';
22
import { makeStyles, createStyles, Theme } from '@material-ui/core/styles';
33
import ChatWindowHeader from './ChatWindowHeader/ChatWindowHeader';
4+
import MessageList from './MessageList/MessageList';
45
import useChatContext from '../../hooks/useChatContext/useChatContext';
56

67
const useStyles = makeStyles((theme: Theme) =>
78
createStyles({
8-
container: {
9+
chatWindowContainer: {
910
overflowY: 'auto',
1011
background: '#FFFFFF',
1112
zIndex: 100,
@@ -22,14 +23,15 @@ const useStyles = makeStyles((theme: Theme) =>
2223

2324
export default function ChatWindow() {
2425
const classes = useStyles();
25-
const { isChatWindowOpen } = useChatContext();
26+
const { isChatWindowOpen, messages } = useChatContext();
2627

2728
//if chat window is not open, don't render this component
2829
if (!isChatWindowOpen) return null;
2930

3031
return (
31-
<aside className={classes.container}>
32+
<aside className={classes.chatWindowContainer}>
3233
<ChatWindowHeader />
34+
<MessageList messages={messages} />
3335
</aside>
3436
);
3537
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import React from 'react';
2+
import { makeStyles, createStyles } from '@material-ui/core/styles';
3+
4+
const useStyles = makeStyles(() =>
5+
createStyles({
6+
messageInfoContainer: {
7+
display: 'flex',
8+
justifyContent: 'space-between',
9+
alignItems: 'center',
10+
padding: '1.425em 0 0.083em',
11+
fontSize: '12px',
12+
color: '#606B85',
13+
},
14+
})
15+
);
16+
17+
interface MessageInfoProps {
18+
author: string;
19+
dateCreated: string;
20+
isLocalParticipant: boolean;
21+
}
22+
23+
export default function MessageInfo({ author, dateCreated, isLocalParticipant }: MessageInfoProps) {
24+
const classes = useStyles();
25+
26+
return (
27+
<div className={classes.messageInfoContainer}>
28+
<div>{isLocalParticipant ? `${author} (You)` : author}</div>
29+
<div>{dateCreated}</div>
30+
</div>
31+
);
32+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import React from 'react';
2+
import { render } from '@testing-library/react';
3+
import MessageList from './MessageList';
4+
5+
jest.mock('../../../hooks/useVideoContext/useVideoContext', () => () => ({
6+
room: { localParticipant: { identity: 'olivia' } },
7+
}));
8+
9+
const arbitraryDate = new Date(1614635301000);
10+
11+
const messages: any = [
12+
{
13+
author: 'olivia',
14+
dateCreated: arbitraryDate,
15+
body: 'This is a message',
16+
sid: 'IMXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
17+
type: 'text',
18+
},
19+
{
20+
author: 'tim',
21+
dateCreated: arbitraryDate,
22+
body: 'Hi Olivia!',
23+
sid: 'IMXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX2',
24+
type: 'text',
25+
},
26+
{
27+
author: 'tim',
28+
dateCreated: arbitraryDate,
29+
body: 'That is pretty rad double line message! How did you do that?',
30+
sid: 'IMXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX3',
31+
type: 'text',
32+
},
33+
{
34+
author: 'tim',
35+
dateCreated: arbitraryDate,
36+
body: '😉',
37+
sid: 'IMXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX4',
38+
type: 'text',
39+
},
40+
{
41+
author: 'olivia',
42+
dateCreated: new Date(1614635361000),
43+
body: 'Magic',
44+
sid: 'IMXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX5',
45+
type: 'text',
46+
},
47+
{
48+
author: 'olivia',
49+
dateCreated: new Date(1614635361000),
50+
body: 'lots of magic',
51+
sid: 'IMXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX6',
52+
type: 'text',
53+
},
54+
];
55+
56+
describe('the messageList component', () => {
57+
it('should render correctly', () => {
58+
const { container } = render(<MessageList messages={messages} />);
59+
expect(container).toMatchSnapshot();
60+
});
61+
});
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import React from 'react';
2+
import TextMessage from './TextMessage/TextMessage';
3+
import MessageInfo from './MessageInfo/MessageInfo';
4+
import { Message } from '@twilio/conversations/lib/message';
5+
import useVideoContext from '../../../hooks/useVideoContext/useVideoContext';
6+
7+
interface MessageListProps {
8+
messages: Message[];
9+
}
10+
11+
export default function MessageList({ messages }: MessageListProps) {
12+
const { room } = useVideoContext();
13+
const localParticipant = room!.localParticipant;
14+
15+
return (
16+
<div style={{ padding: '0 1.2em' }}>
17+
{messages.map((message, idx) => {
18+
const time = message.dateCreated
19+
.toLocaleTimeString('en-us', { hour: 'numeric', minute: 'numeric' })
20+
.toLowerCase();
21+
const isLocalParticipant = localParticipant.identity === message.author;
22+
23+
return (
24+
<React.Fragment key={message.sid}>
25+
{messages[idx - 1]?.author !== message.author && (
26+
<MessageInfo author={message.author} isLocalParticipant={isLocalParticipant} dateCreated={time} />
27+
)}
28+
{message.type === 'text' && <TextMessage body={message.body} isLocalParticipant={isLocalParticipant} />}
29+
</React.Fragment>
30+
);
31+
})}
32+
</div>
33+
);
34+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import React from 'react';
2+
import clsx from 'clsx';
3+
import { makeStyles } from '@material-ui/core/styles';
4+
5+
const useStyles = makeStyles({
6+
messageContainer: {
7+
borderRadius: '16px',
8+
display: 'inline-flex',
9+
alignItems: 'center',
10+
padding: '0.5em 0.8em 0.6em',
11+
margin: '0.3em 0 0',
12+
wordBreak: 'break-word',
13+
backgroundColor: '#E1E3EA',
14+
hyphens: 'auto',
15+
},
16+
isLocalParticipant: {
17+
backgroundColor: '#CCE4FF',
18+
},
19+
});
20+
21+
interface TextMessageProps {
22+
body: string;
23+
isLocalParticipant: boolean;
24+
}
25+
26+
export default function TextMessage({ body, isLocalParticipant }: TextMessageProps) {
27+
const classes = useStyles();
28+
29+
return (
30+
<div>
31+
<div
32+
className={clsx(classes.messageContainer, {
33+
[classes.isLocalParticipant]: isLocalParticipant,
34+
})}
35+
>
36+
{body}
37+
</div>
38+
</div>
39+
);
40+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`the messageList component should render correctly 1`] = `
4+
<div>
5+
<div
6+
style="padding: 0px 1.2em;"
7+
>
8+
<div
9+
class="makeStyles-messageInfoContainer-1"
10+
>
11+
<div>
12+
olivia (You)
13+
</div>
14+
<div>
15+
9:48 pm
16+
</div>
17+
</div>
18+
<div>
19+
<div
20+
class="makeStyles-messageContainer-2 makeStyles-isLocalParticipant-3"
21+
>
22+
This is a message
23+
</div>
24+
</div>
25+
<div
26+
class="makeStyles-messageInfoContainer-1"
27+
>
28+
<div>
29+
tim
30+
</div>
31+
<div>
32+
9:48 pm
33+
</div>
34+
</div>
35+
<div>
36+
<div
37+
class="makeStyles-messageContainer-2"
38+
>
39+
Hi Olivia!
40+
</div>
41+
</div>
42+
<div>
43+
<div
44+
class="makeStyles-messageContainer-2"
45+
>
46+
That is pretty rad double line message! How did you do that?
47+
</div>
48+
</div>
49+
<div>
50+
<div
51+
class="makeStyles-messageContainer-2"
52+
>
53+
😉
54+
</div>
55+
</div>
56+
<div
57+
class="makeStyles-messageInfoContainer-1"
58+
>
59+
<div>
60+
olivia (You)
61+
</div>
62+
<div>
63+
9:49 pm
64+
</div>
65+
</div>
66+
<div>
67+
<div
68+
class="makeStyles-messageContainer-2 makeStyles-isLocalParticipant-3"
69+
>
70+
Magic
71+
</div>
72+
</div>
73+
<div>
74+
<div
75+
class="makeStyles-messageContainer-2 makeStyles-isLocalParticipant-3"
76+
>
77+
lots of magic
78+
</div>
79+
</div>
80+
</div>
81+
</div>
82+
`;

0 commit comments

Comments
 (0)