blob: e36b0da5ea2fdd37e9f9c9aeaf04a5b88c77e4c9 [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';
simon07b4eb02022-09-29 17:50:26 -04008
simond47ef9e2022-09-28 22:24:28 -04009import { EmojiButton, MoreButton, ReplyMessageButton } from './buttons';
10import ConversationAvatar from './ConversationAvatar';
11import { OppositeArrowsIcon, TrashBinIcon, TwoSheetsIcon } from './svgIcons';
Larbi Gharibe9af9732021-03-31 15:08:01 +010012
simond47ef9e2022-09-28 22:24:28 -040013dayjs.extend(isToday);
14dayjs.extend(isYesterday);
idillonbef18a52022-09-01 01:51:40 -040015
16export const MessageCall = (props) => {
simon80b7b3b2022-09-28 17:50:10 -040017 return <Stack alignItems="center">&quot;Appel&quot;</Stack>;
simond47ef9e2022-09-28 22:24:28 -040018};
Larbi Gharibe9af9732021-03-31 15:08:01 +010019
idillonbef18a52022-09-01 01:51:40 -040020export const MessageInitial = (props) => {
simond47ef9e2022-09-28 22:24:28 -040021 const { t } = useTranslation();
22 return <Stack alignItems="center">{t('message_swarm_created')}</Stack>;
23};
idillonbef18a52022-09-01 01:51:40 -040024
25export const MessageDataTransfer = (props) => {
26 return (
27 <MessageBubble
simond47ef9e2022-09-28 22:24:28 -040028 backgroundColor={'#E5E5E5'}
idillonbef18a52022-09-01 01:51:40 -040029 position={props.position}
30 isFirstOfGroup={props.isFirstOfGroup}
31 isLastOfGroup={props.isLastOfGroup}
32 >
simon80b7b3b2022-09-28 17:50:10 -040033 &quot;data-transfer&quot;
idillonbef18a52022-09-01 01:51:40 -040034 </MessageBubble>
simond47ef9e2022-09-28 22:24:28 -040035 );
36};
idillonbef18a52022-09-01 01:51:40 -040037
38export const MessageMember = (props) => {
simond47ef9e2022-09-28 22:24:28 -040039 const { t } = useTranslation();
idillonbef18a52022-09-01 01:51:40 -040040 return (
simond47ef9e2022-09-28 22:24:28 -040041 <Stack alignItems="center">
idillonbef18a52022-09-01 01:51:40 -040042 <Chip
43 sx={{
simond47ef9e2022-09-28 22:24:28 -040044 width: 'fit-content',
idillonbef18a52022-09-01 01:51:40 -040045 }}
simond47ef9e2022-09-28 22:24:28 -040046 label={t('message_user_joined', { user: props.message.author })}
idillonbef18a52022-09-01 01:51:40 -040047 />
48 </Stack>
simond47ef9e2022-09-28 22:24:28 -040049 );
50};
idillonbef18a52022-09-01 01:51:40 -040051
52export const MessageMerge = (props) => {
simon80b7b3b2022-09-28 17:50:10 -040053 return <Stack alignItems="center">&quot;merge&quot;</Stack>;
simond47ef9e2022-09-28 22:24:28 -040054};
idillonbef18a52022-09-01 01:51:40 -040055
56export const MessageText = (props) => {
57 return (
58 <MessageBubble
59 backgroundColor={props.bubbleColor}
60 position={props.position}
61 isFirstOfGroup={props.isFirstOfGroup}
62 isLastOfGroup={props.isLastOfGroup}
63 >
idillon89720a82022-09-06 18:47:05 -040064 <Typography variant="body1" color={props.textColor} textAlign={props.position}>
idillonbef18a52022-09-01 01:51:40 -040065 {props.message.body}
66 </Typography>
67 </MessageBubble>
simond47ef9e2022-09-28 22:24:28 -040068 );
69};
idillonbef18a52022-09-01 01:51:40 -040070
simond47ef9e2022-09-28 22:24:28 -040071export const MessageDate = ({ time }) => {
72 let textDate;
idillonbef18a52022-09-01 01:51:40 -040073
74 if (time.isToday()) {
simond47ef9e2022-09-28 22:24:28 -040075 textDate = 'Today';
76 } else if (time.isYesterday()) {
77 textDate = 'Yesterday';
78 } else {
79 const date = time.date().toString().padStart(2, '0');
80 const month = (time.month() + 1).toString().padStart(2, '0');
81 textDate = `${date}/${month}/${time.year()}`;
idillonbef18a52022-09-01 01:51:40 -040082 }
83
84 return (
simond47ef9e2022-09-28 22:24:28 -040085 <Box marginTop="30px">
idillon04245a12022-09-01 11:12:17 -040086 <Divider
87 sx={{
simond47ef9e2022-09-28 22:24:28 -040088 '.MuiDivider-wrapper': {
idillon04245a12022-09-01 11:12:17 -040089 margin: 0,
90 padding: 0,
91 },
simond47ef9e2022-09-28 22:24:28 -040092 '&::before': {
93 borderTop: '1px solid #E5E5E5',
idillon04245a12022-09-01 11:12:17 -040094 },
simond47ef9e2022-09-28 22:24:28 -040095 '&::after': {
96 borderTop: '1px solid #E5E5E5',
idillon04245a12022-09-01 11:12:17 -040097 },
98 }}
99 >
100 <Typography
101 variant="caption"
102 fontWeight={700}
103 border="1px solid #E5E5E5"
104 borderRadius="5px"
105 padding="10px 16px"
106 >
107 {textDate}
108 </Typography>
idillonbef18a52022-09-01 01:51:40 -0400109 </Divider>
110 </Box>
simond47ef9e2022-09-28 22:24:28 -0400111 );
112};
idillonbef18a52022-09-01 01:51:40 -0400113
simond47ef9e2022-09-28 22:24:28 -0400114export const MessageTime = ({ time, hasDateOnTop }) => {
115 const hour = time.hour().toString().padStart(2, '0');
116 const minute = time.minute().toString().padStart(2, '0');
117 const textTime = `${hour}:${minute}`;
idillonbef18a52022-09-01 01:51:40 -0400118
119 return (
simond47ef9e2022-09-28 22:24:28 -0400120 <Stack direction="row" justifyContent="center" margin="30px" marginTop={hasDateOnTop ? '20px' : '30px'}>
121 <Typography variant="caption" color="#A7A7A7" fontWeight={700}>
idillonbef18a52022-09-01 01:51:40 -0400122 {textTime}
123 </Typography>
124 </Stack>
simond47ef9e2022-09-28 22:24:28 -0400125 );
126};
idillonbef18a52022-09-01 01:51:40 -0400127
128export const MessageBubblesGroup = (props) => {
simond47ef9e2022-09-28 22:24:28 -0400129 const isUser = false; // should access user from the store
130 const position = isUser ? 'end' : 'start';
131 const bubbleColor = isUser ? '#005699' : '#E5E5E5';
132 const textColor = isUser ? 'white' : 'black';
idillonbef18a52022-09-01 01:51:40 -0400133
134 return (
idillon89720a82022-09-06 18:47:05 -0400135 <Stack // Row for a group of message bubbles with the user's infos
idillonbef18a52022-09-01 01:51:40 -0400136 direction="row"
137 justifyContent={position}
idillon89720a82022-09-06 18:47:05 -0400138 alignItems="end"
139 spacing="10px"
idillonbef18a52022-09-01 01:51:40 -0400140 >
simond47ef9e2022-09-28 22:24:28 -0400141 {!isUser && (
142 <ConversationAvatar displayName="TempDisplayName" sx={{ width: '22px', height: '22px', fontSize: '15px' }} />
143 )}
idillon89720a82022-09-06 18:47:05 -0400144 <Stack // Container to align the bubbles to the same side of a row
idillonbef18a52022-09-01 01:51:40 -0400145 width="66.66%"
idillonbef18a52022-09-01 01:51:40 -0400146 alignItems={position}
147 >
simond47ef9e2022-09-28 22:24:28 -0400148 <ParticipantName name={props.messages[0]?.author} position={position} />
idillon89720a82022-09-06 18:47:05 -0400149 <Stack // Container for a group of message bubbles
idillonbef18a52022-09-01 01:51:40 -0400150 spacing="6px"
151 alignItems={position}
152 direction="column-reverse"
153 >
simond47ef9e2022-09-28 22:24:28 -0400154 {props.messages.map((message, index) => {
155 let Component;
156 switch (message.type) {
157 case 'text/plain':
158 Component = MessageText;
159 break;
160 case 'application/data-transfer+json':
161 Component = MessageDataTransfer;
162 break;
idillonbef18a52022-09-01 01:51:40 -0400163 }
simond47ef9e2022-09-28 22:24:28 -0400164 return (
165 <Component // Single message
166 key={message.id}
167 message={message}
168 textColor={textColor}
169 position={position}
170 bubbleColor={bubbleColor}
171 isFirstOfGroup={index == props.messages.length - 1}
172 isLastOfGroup={index == 0}
173 />
174 );
175 })}
idillonbef18a52022-09-01 01:51:40 -0400176 </Stack>
177 </Stack>
178 </Stack>
simond47ef9e2022-09-28 22:24:28 -0400179 );
180};
idillonbef18a52022-09-01 01:51:40 -0400181
idillon927b7592022-09-15 12:56:45 -0400182const MessageTooltip = styled(({ className, ...props }) => {
simond47ef9e2022-09-28 22:24:28 -0400183 const [open, setOpen] = useState(false);
184 const emojis = ['😎', '😄', '😍']; // Should be last three used emojis
idillon927b7592022-09-15 12:56:45 -0400185 const additionalOptions = [
186 {
187 Icon: TwoSheetsIcon,
simond47ef9e2022-09-28 22:24:28 -0400188 text: 'Copy',
idillon927b7592022-09-15 12:56:45 -0400189 action: () => {},
190 },
191 {
192 Icon: OppositeArrowsIcon,
simond47ef9e2022-09-28 22:24:28 -0400193 text: 'Transfer',
idillon927b7592022-09-15 12:56:45 -0400194 action: () => {},
195 },
196 {
197 Icon: TrashBinIcon,
simond47ef9e2022-09-28 22:24:28 -0400198 text: 'Delete message',
idillon927b7592022-09-15 12:56:45 -0400199 action: () => {},
200 },
simond47ef9e2022-09-28 22:24:28 -0400201 ];
idillon927b7592022-09-15 12:56:45 -0400202
simond47ef9e2022-09-28 22:24:28 -0400203 const toggleMoreMenu = useCallback(() => setOpen((open) => !open), [setOpen]);
idillon927b7592022-09-15 12:56:45 -0400204
simond47ef9e2022-09-28 22:24:28 -0400205 const onClose = useCallback(() => {
206 setOpen(false);
207 }, [setOpen]);
idillon927b7592022-09-15 12:56:45 -0400208
209 return (
210 <Tooltip
211 {...props}
212 classes={{ tooltip: className }} // Required for styles. Don't know why
simond47ef9e2022-09-28 22:24:28 -0400213 placement={props.position == 'start' ? 'right-start' : 'left-start'}
idillon927b7592022-09-15 12:56:45 -0400214 PopperProps={{
215 modifiers: [
216 {
simond47ef9e2022-09-28 22:24:28 -0400217 name: 'offset',
idillon927b7592022-09-15 12:56:45 -0400218 options: {
simond47ef9e2022-09-28 22:24:28 -0400219 offset: [-2, -30],
idillon927b7592022-09-15 12:56:45 -0400220 },
221 },
222 ],
223 }}
224 onClose={onClose}
225 title={
simond47ef9e2022-09-28 22:24:28 -0400226 <Stack>
227 {' '}
228 {/* Whole tooltip's content */}
idillon927b7592022-09-15 12:56:45 -0400229 <Stack // Main options
230 direction="row"
231 spacing="16px"
232 >
simond47ef9e2022-09-28 22:24:28 -0400233 {emojis.map((emoji) => (
234 <EmojiButton key={emoji} emoji={emoji} />
235 ))}
236 <ReplyMessageButton />
237 <MoreButton onClick={toggleMoreMenu} />
idillon927b7592022-09-15 12:56:45 -0400238 </Stack>
simond47ef9e2022-09-28 22:24:28 -0400239 {open && ( // Additional menu options
idillon927b7592022-09-15 12:56:45 -0400240 <>
simond47ef9e2022-09-28 22:24:28 -0400241 <Divider sx={{ paddingTop: '16px' }} />
242 <List sx={{ padding: 0, paddingTop: '8px', marginBottom: '-8px' }}>
243 {additionalOptions.map((option) => (
244 <ListItemButton
245 key={option.text}
246 sx={{
247 padding: '8px',
248 }}
249 >
250 <Stack // Could not find proper way to set spacing between ListItemIcon and ListItemText
251 direction="row"
252 spacing="16px"
idillon927b7592022-09-15 12:56:45 -0400253 >
simond47ef9e2022-09-28 22:24:28 -0400254 <option.Icon
255 sx={{
256 height: '16px',
257 margin: 0,
258 color: (theme) => theme.palette.primary.dark,
259 }}
260 />
261 <ListItemText
262 primary={option.text}
263 primaryTypographyProps={{
264 fontSize: '12px',
265 lineHeight: '16px',
266 }}
267 sx={{
268 height: '16px',
269 margin: 0,
270 }}
271 />
272 </Stack>
273 </ListItemButton>
274 ))}
275 </List>
idillon927b7592022-09-15 12:56:45 -0400276 </>
simond47ef9e2022-09-28 22:24:28 -0400277 )}
idillon927b7592022-09-15 12:56:45 -0400278 </Stack>
279 }
280 />
simond47ef9e2022-09-28 22:24:28 -0400281 );
idillon927b7592022-09-15 12:56:45 -0400282})(({ theme, position }) => {
simond47ef9e2022-09-28 22:24:28 -0400283 const largeRadius = '20px';
284 const smallRadius = '5px';
idillon927b7592022-09-15 12:56:45 -0400285 return {
simond47ef9e2022-09-28 22:24:28 -0400286 backgroundColor: 'white',
287 padding: '16px',
288 boxShadow: '3px 3px 7px #00000029',
idillon927b7592022-09-15 12:56:45 -0400289 borderRadius: largeRadius,
simond47ef9e2022-09-28 22:24:28 -0400290 borderStartStartRadius: position == 'start' ? smallRadius : largeRadius,
291 borderStartEndRadius: position == 'end' ? smallRadius : largeRadius,
292 };
idillon927b7592022-09-15 12:56:45 -0400293});
294
idillonbef18a52022-09-01 01:51:40 -0400295const MessageBubble = (props) => {
simond47ef9e2022-09-28 22:24:28 -0400296 const largeRadius = '20px';
297 const smallRadius = '5px';
Adrien Béraud023f7cf2022-09-18 14:57:53 -0400298 const radius = useMemo(() => {
simond47ef9e2022-09-28 22:24:28 -0400299 if (props.position == 'start') {
idillonbef18a52022-09-01 01:51:40 -0400300 return {
301 borderStartStartRadius: props.isFirstOfGroup ? largeRadius : smallRadius,
302 borderStartEndRadius: largeRadius,
303 borderEndStartRadius: props.isLastOfGroup ? largeRadius : smallRadius,
304 borderEndEndRadius: largeRadius,
simond47ef9e2022-09-28 22:24:28 -0400305 };
idillonbef18a52022-09-01 01:51:40 -0400306 }
307 return {
308 borderStartStartRadius: largeRadius,
309 borderStartEndRadius: props.isFirstOfGroup ? largeRadius : smallRadius,
310 borderEndStartRadius: largeRadius,
311 borderEndEndRadius: props.isLastOfGroup ? largeRadius : smallRadius,
simond47ef9e2022-09-28 22:24:28 -0400312 };
313 }, [props.isFirstOfGroup, props.isLastOfGroup, props.position]);
idillonbef18a52022-09-01 01:51:40 -0400314
315 return (
simond47ef9e2022-09-28 22:24:28 -0400316 <MessageTooltip position={props.position}>
idillon927b7592022-09-15 12:56:45 -0400317 <Box
318 sx={{
simond47ef9e2022-09-28 22:24:28 -0400319 width: 'fit-content',
idillon927b7592022-09-15 12:56:45 -0400320 backgroundColor: props.backgroundColor,
simond47ef9e2022-09-28 22:24:28 -0400321 padding: '16px',
idillon927b7592022-09-15 12:56:45 -0400322 ...radius,
323 }}
324 >
325 {props.children}
326 </Box>
327 </MessageTooltip>
simond47ef9e2022-09-28 22:24:28 -0400328 );
329};
idillonbef18a52022-09-01 01:51:40 -0400330
331const ParticipantName = (props) => {
332 return (
simond47ef9e2022-09-28 22:24:28 -0400333 <Box marginBottom="6px" marginLeft="16px" marginRight="16px">
334 <Typography variant="caption" color="#A7A7A7" fontWeight={700}>
idillonbef18a52022-09-01 01:51:40 -0400335 {props.name}
336 </Typography>
337 </Box>
simond47ef9e2022-09-28 22:24:28 -0400338 );
339};