blob: 7626c6ea3934b407fbe79ad53a8c5aae1dbede60 [file] [log] [blame]
simond47ef9e2022-09-28 22:24:28 -04001import { Box, Chip, Divider, List, ListItemButton, ListItemText, Stack, Tooltip, Typography } from '@mui/material';
2import { styled } from '@mui/material/styles';
3import dayjs from 'dayjs';
4import isToday from 'dayjs/plugin/isToday';
5import isYesterday from 'dayjs/plugin/isYesterday';
6import React, { useCallback, useMemo, useState } from 'react';
7import { useTranslation } from 'react-i18next';
8import { EmojiButton, MoreButton, ReplyMessageButton } from './buttons';
9import ConversationAvatar from './ConversationAvatar';
10import { OppositeArrowsIcon, TrashBinIcon, TwoSheetsIcon } from './svgIcons';
Larbi Gharibe9af9732021-03-31 15:08:01 +010011
simond47ef9e2022-09-28 22:24:28 -040012dayjs.extend(isToday);
13dayjs.extend(isYesterday);
idillonbef18a52022-09-01 01:51:40 -040014
15export const MessageCall = (props) => {
simond47ef9e2022-09-28 22:24:28 -040016 return <Stack alignItems="center">"Appel"</Stack>;
17};
Larbi Gharibe9af9732021-03-31 15:08:01 +010018
idillonbef18a52022-09-01 01:51:40 -040019export const MessageInitial = (props) => {
simond47ef9e2022-09-28 22:24:28 -040020 const { t } = useTranslation();
21 return <Stack alignItems="center">{t('message_swarm_created')}</Stack>;
22};
idillonbef18a52022-09-01 01:51:40 -040023
24export const MessageDataTransfer = (props) => {
25 return (
26 <MessageBubble
simond47ef9e2022-09-28 22:24:28 -040027 backgroundColor={'#E5E5E5'}
idillonbef18a52022-09-01 01:51:40 -040028 position={props.position}
29 isFirstOfGroup={props.isFirstOfGroup}
30 isLastOfGroup={props.isLastOfGroup}
31 >
32 "data-transfer"
33 </MessageBubble>
simond47ef9e2022-09-28 22:24:28 -040034 );
35};
idillonbef18a52022-09-01 01:51:40 -040036
37export const MessageMember = (props) => {
simond47ef9e2022-09-28 22:24:28 -040038 const { t } = useTranslation();
idillonbef18a52022-09-01 01:51:40 -040039 return (
simond47ef9e2022-09-28 22:24:28 -040040 <Stack alignItems="center">
idillonbef18a52022-09-01 01:51:40 -040041 <Chip
42 sx={{
simond47ef9e2022-09-28 22:24:28 -040043 width: 'fit-content',
idillonbef18a52022-09-01 01:51:40 -040044 }}
simond47ef9e2022-09-28 22:24:28 -040045 label={t('message_user_joined', { user: props.message.author })}
idillonbef18a52022-09-01 01:51:40 -040046 />
47 </Stack>
simond47ef9e2022-09-28 22:24:28 -040048 );
49};
idillonbef18a52022-09-01 01:51:40 -040050
51export const MessageMerge = (props) => {
simond47ef9e2022-09-28 22:24:28 -040052 return <Stack alignItems="center">"merge"</Stack>;
53};
idillonbef18a52022-09-01 01:51:40 -040054
55export const MessageText = (props) => {
56 return (
57 <MessageBubble
58 backgroundColor={props.bubbleColor}
59 position={props.position}
60 isFirstOfGroup={props.isFirstOfGroup}
61 isLastOfGroup={props.isLastOfGroup}
62 >
idillon89720a82022-09-06 18:47:05 -040063 <Typography variant="body1" color={props.textColor} textAlign={props.position}>
idillonbef18a52022-09-01 01:51:40 -040064 {props.message.body}
65 </Typography>
66 </MessageBubble>
simond47ef9e2022-09-28 22:24:28 -040067 );
68};
idillonbef18a52022-09-01 01:51:40 -040069
simond47ef9e2022-09-28 22:24:28 -040070export const MessageDate = ({ time }) => {
71 let textDate;
idillonbef18a52022-09-01 01:51:40 -040072
73 if (time.isToday()) {
simond47ef9e2022-09-28 22:24:28 -040074 textDate = 'Today';
75 } else if (time.isYesterday()) {
76 textDate = 'Yesterday';
77 } else {
78 const date = time.date().toString().padStart(2, '0');
79 const month = (time.month() + 1).toString().padStart(2, '0');
80 textDate = `${date}/${month}/${time.year()}`;
idillonbef18a52022-09-01 01:51:40 -040081 }
82
83 return (
simond47ef9e2022-09-28 22:24:28 -040084 <Box marginTop="30px">
idillon04245a12022-09-01 11:12:17 -040085 <Divider
86 sx={{
simond47ef9e2022-09-28 22:24:28 -040087 '.MuiDivider-wrapper': {
idillon04245a12022-09-01 11:12:17 -040088 margin: 0,
89 padding: 0,
90 },
simond47ef9e2022-09-28 22:24:28 -040091 '&::before': {
92 borderTop: '1px solid #E5E5E5',
idillon04245a12022-09-01 11:12:17 -040093 },
simond47ef9e2022-09-28 22:24:28 -040094 '&::after': {
95 borderTop: '1px solid #E5E5E5',
idillon04245a12022-09-01 11:12:17 -040096 },
97 }}
98 >
99 <Typography
100 variant="caption"
101 fontWeight={700}
102 border="1px solid #E5E5E5"
103 borderRadius="5px"
104 padding="10px 16px"
105 >
106 {textDate}
107 </Typography>
idillonbef18a52022-09-01 01:51:40 -0400108 </Divider>
109 </Box>
simond47ef9e2022-09-28 22:24:28 -0400110 );
111};
idillonbef18a52022-09-01 01:51:40 -0400112
simond47ef9e2022-09-28 22:24:28 -0400113export const MessageTime = ({ time, hasDateOnTop }) => {
114 const hour = time.hour().toString().padStart(2, '0');
115 const minute = time.minute().toString().padStart(2, '0');
116 const textTime = `${hour}:${minute}`;
idillonbef18a52022-09-01 01:51:40 -0400117
118 return (
simond47ef9e2022-09-28 22:24:28 -0400119 <Stack direction="row" justifyContent="center" margin="30px" marginTop={hasDateOnTop ? '20px' : '30px'}>
120 <Typography variant="caption" color="#A7A7A7" fontWeight={700}>
idillonbef18a52022-09-01 01:51:40 -0400121 {textTime}
122 </Typography>
123 </Stack>
simond47ef9e2022-09-28 22:24:28 -0400124 );
125};
idillonbef18a52022-09-01 01:51:40 -0400126
127export const MessageBubblesGroup = (props) => {
simond47ef9e2022-09-28 22:24:28 -0400128 const isUser = false; // should access user from the store
129 const position = isUser ? 'end' : 'start';
130 const bubbleColor = isUser ? '#005699' : '#E5E5E5';
131 const textColor = isUser ? 'white' : 'black';
idillonbef18a52022-09-01 01:51:40 -0400132
133 return (
idillon89720a82022-09-06 18:47:05 -0400134 <Stack // Row for a group of message bubbles with the user's infos
idillonbef18a52022-09-01 01:51:40 -0400135 direction="row"
136 justifyContent={position}
idillon89720a82022-09-06 18:47:05 -0400137 alignItems="end"
138 spacing="10px"
idillonbef18a52022-09-01 01:51:40 -0400139 >
simond47ef9e2022-09-28 22:24:28 -0400140 {!isUser && (
141 <ConversationAvatar displayName="TempDisplayName" sx={{ width: '22px', height: '22px', fontSize: '15px' }} />
142 )}
idillon89720a82022-09-06 18:47:05 -0400143 <Stack // Container to align the bubbles to the same side of a row
idillonbef18a52022-09-01 01:51:40 -0400144 width="66.66%"
idillonbef18a52022-09-01 01:51:40 -0400145 alignItems={position}
146 >
simond47ef9e2022-09-28 22:24:28 -0400147 <ParticipantName name={props.messages[0]?.author} position={position} />
idillon89720a82022-09-06 18:47:05 -0400148 <Stack // Container for a group of message bubbles
idillonbef18a52022-09-01 01:51:40 -0400149 spacing="6px"
150 alignItems={position}
151 direction="column-reverse"
152 >
simond47ef9e2022-09-28 22:24:28 -0400153 {props.messages.map((message, index) => {
154 let Component;
155 switch (message.type) {
156 case 'text/plain':
157 Component = MessageText;
158 break;
159 case 'application/data-transfer+json':
160 Component = MessageDataTransfer;
161 break;
idillonbef18a52022-09-01 01:51:40 -0400162 }
simond47ef9e2022-09-28 22:24:28 -0400163 return (
164 <Component // Single message
165 key={message.id}
166 message={message}
167 textColor={textColor}
168 position={position}
169 bubbleColor={bubbleColor}
170 isFirstOfGroup={index == props.messages.length - 1}
171 isLastOfGroup={index == 0}
172 />
173 );
174 })}
idillonbef18a52022-09-01 01:51:40 -0400175 </Stack>
176 </Stack>
177 </Stack>
simond47ef9e2022-09-28 22:24:28 -0400178 );
179};
idillonbef18a52022-09-01 01:51:40 -0400180
idillon927b7592022-09-15 12:56:45 -0400181const MessageTooltip = styled(({ className, ...props }) => {
simond47ef9e2022-09-28 22:24:28 -0400182 const [open, setOpen] = useState(false);
183 const emojis = ['😎', '😄', '😍']; // Should be last three used emojis
idillon927b7592022-09-15 12:56:45 -0400184 const additionalOptions = [
185 {
186 Icon: TwoSheetsIcon,
simond47ef9e2022-09-28 22:24:28 -0400187 text: 'Copy',
idillon927b7592022-09-15 12:56:45 -0400188 action: () => {},
189 },
190 {
191 Icon: OppositeArrowsIcon,
simond47ef9e2022-09-28 22:24:28 -0400192 text: 'Transfer',
idillon927b7592022-09-15 12:56:45 -0400193 action: () => {},
194 },
195 {
196 Icon: TrashBinIcon,
simond47ef9e2022-09-28 22:24:28 -0400197 text: 'Delete message',
idillon927b7592022-09-15 12:56:45 -0400198 action: () => {},
199 },
simond47ef9e2022-09-28 22:24:28 -0400200 ];
idillon927b7592022-09-15 12:56:45 -0400201
simond47ef9e2022-09-28 22:24:28 -0400202 const toggleMoreMenu = useCallback(() => setOpen((open) => !open), [setOpen]);
idillon927b7592022-09-15 12:56:45 -0400203
simond47ef9e2022-09-28 22:24:28 -0400204 const onClose = useCallback(() => {
205 setOpen(false);
206 }, [setOpen]);
idillon927b7592022-09-15 12:56:45 -0400207
208 return (
209 <Tooltip
210 {...props}
211 classes={{ tooltip: className }} // Required for styles. Don't know why
simond47ef9e2022-09-28 22:24:28 -0400212 placement={props.position == 'start' ? 'right-start' : 'left-start'}
idillon927b7592022-09-15 12:56:45 -0400213 PopperProps={{
214 modifiers: [
215 {
simond47ef9e2022-09-28 22:24:28 -0400216 name: 'offset',
idillon927b7592022-09-15 12:56:45 -0400217 options: {
simond47ef9e2022-09-28 22:24:28 -0400218 offset: [-2, -30],
idillon927b7592022-09-15 12:56:45 -0400219 },
220 },
221 ],
222 }}
223 onClose={onClose}
224 title={
simond47ef9e2022-09-28 22:24:28 -0400225 <Stack>
226 {' '}
227 {/* Whole tooltip's content */}
idillon927b7592022-09-15 12:56:45 -0400228 <Stack // Main options
229 direction="row"
230 spacing="16px"
231 >
simond47ef9e2022-09-28 22:24:28 -0400232 {emojis.map((emoji) => (
233 <EmojiButton key={emoji} emoji={emoji} />
234 ))}
235 <ReplyMessageButton />
236 <MoreButton onClick={toggleMoreMenu} />
idillon927b7592022-09-15 12:56:45 -0400237 </Stack>
simond47ef9e2022-09-28 22:24:28 -0400238 {open && ( // Additional menu options
idillon927b7592022-09-15 12:56:45 -0400239 <>
simond47ef9e2022-09-28 22:24:28 -0400240 <Divider sx={{ paddingTop: '16px' }} />
241 <List sx={{ padding: 0, paddingTop: '8px', marginBottom: '-8px' }}>
242 {additionalOptions.map((option) => (
243 <ListItemButton
244 key={option.text}
245 sx={{
246 padding: '8px',
247 }}
248 >
249 <Stack // Could not find proper way to set spacing between ListItemIcon and ListItemText
250 direction="row"
251 spacing="16px"
idillon927b7592022-09-15 12:56:45 -0400252 >
simond47ef9e2022-09-28 22:24:28 -0400253 <option.Icon
254 sx={{
255 height: '16px',
256 margin: 0,
257 color: (theme) => theme.palette.primary.dark,
258 }}
259 />
260 <ListItemText
261 primary={option.text}
262 primaryTypographyProps={{
263 fontSize: '12px',
264 lineHeight: '16px',
265 }}
266 sx={{
267 height: '16px',
268 margin: 0,
269 }}
270 />
271 </Stack>
272 </ListItemButton>
273 ))}
274 </List>
idillon927b7592022-09-15 12:56:45 -0400275 </>
simond47ef9e2022-09-28 22:24:28 -0400276 )}
idillon927b7592022-09-15 12:56:45 -0400277 </Stack>
278 }
279 />
simond47ef9e2022-09-28 22:24:28 -0400280 );
idillon927b7592022-09-15 12:56:45 -0400281})(({ theme, position }) => {
simond47ef9e2022-09-28 22:24:28 -0400282 const largeRadius = '20px';
283 const smallRadius = '5px';
idillon927b7592022-09-15 12:56:45 -0400284 return {
simond47ef9e2022-09-28 22:24:28 -0400285 backgroundColor: 'white',
286 padding: '16px',
287 boxShadow: '3px 3px 7px #00000029',
idillon927b7592022-09-15 12:56:45 -0400288 borderRadius: largeRadius,
simond47ef9e2022-09-28 22:24:28 -0400289 borderStartStartRadius: position == 'start' ? smallRadius : largeRadius,
290 borderStartEndRadius: position == 'end' ? smallRadius : largeRadius,
291 };
idillon927b7592022-09-15 12:56:45 -0400292});
293
idillonbef18a52022-09-01 01:51:40 -0400294const MessageBubble = (props) => {
simond47ef9e2022-09-28 22:24:28 -0400295 const largeRadius = '20px';
296 const smallRadius = '5px';
Adrien Béraud023f7cf2022-09-18 14:57:53 -0400297 const radius = useMemo(() => {
simond47ef9e2022-09-28 22:24:28 -0400298 if (props.position == 'start') {
idillonbef18a52022-09-01 01:51:40 -0400299 return {
300 borderStartStartRadius: props.isFirstOfGroup ? largeRadius : smallRadius,
301 borderStartEndRadius: largeRadius,
302 borderEndStartRadius: props.isLastOfGroup ? largeRadius : smallRadius,
303 borderEndEndRadius: largeRadius,
simond47ef9e2022-09-28 22:24:28 -0400304 };
idillonbef18a52022-09-01 01:51:40 -0400305 }
306 return {
307 borderStartStartRadius: largeRadius,
308 borderStartEndRadius: props.isFirstOfGroup ? largeRadius : smallRadius,
309 borderEndStartRadius: largeRadius,
310 borderEndEndRadius: props.isLastOfGroup ? largeRadius : smallRadius,
simond47ef9e2022-09-28 22:24:28 -0400311 };
312 }, [props.isFirstOfGroup, props.isLastOfGroup, props.position]);
idillonbef18a52022-09-01 01:51:40 -0400313
314 return (
simond47ef9e2022-09-28 22:24:28 -0400315 <MessageTooltip position={props.position}>
idillon927b7592022-09-15 12:56:45 -0400316 <Box
317 sx={{
simond47ef9e2022-09-28 22:24:28 -0400318 width: 'fit-content',
idillon927b7592022-09-15 12:56:45 -0400319 backgroundColor: props.backgroundColor,
simond47ef9e2022-09-28 22:24:28 -0400320 padding: '16px',
idillon927b7592022-09-15 12:56:45 -0400321 ...radius,
322 }}
323 >
324 {props.children}
325 </Box>
326 </MessageTooltip>
simond47ef9e2022-09-28 22:24:28 -0400327 );
328};
idillonbef18a52022-09-01 01:51:40 -0400329
330const ParticipantName = (props) => {
331 return (
simond47ef9e2022-09-28 22:24:28 -0400332 <Box marginBottom="6px" marginLeft="16px" marginRight="16px">
333 <Typography variant="caption" color="#A7A7A7" fontWeight={700}>
idillonbef18a52022-09-01 01:51:40 -0400334 {props.name}
335 </Typography>
336 </Box>
simond47ef9e2022-09-28 22:24:28 -0400337 );
338};