blob: c7f22f1b5630f858b8182d37c8d98dfb299f488c [file] [log] [blame]
idillon927b7592022-09-15 12:56:45 -04001import { Box, Chip, Divider, List, ListItemButton, ListItemText, Stack, Tooltip, Typography } from "@mui/material"
2import { styled } from "@mui/material/styles";
idillonbef18a52022-09-01 01:51:40 -04003import dayjs from "dayjs"
4import isToday from "dayjs/plugin/isToday"
5import isYesterday from "dayjs/plugin/isYesterday"
idillon927b7592022-09-15 12:56:45 -04006import React, { useCallback, useMemo, useState } from "react"
idillon5815c732022-09-16 13:54:45 -04007import { useTranslation } from "react-i18next"
idillon927b7592022-09-15 12:56:45 -04008import { EmojiButton, MoreButton, ReplyMessageButton } from "./buttons";
idillon89720a82022-09-06 18:47:05 -04009import ConversationAvatar from "./ConversationAvatar"
idillon927b7592022-09-15 12:56:45 -040010import { OppositeArrowsIcon, TrashBinIcon, TwoSheetsIcon } from "./svgIcons"
Larbi Gharibe9af9732021-03-31 15:08:01 +010011
idillonbef18a52022-09-01 01:51:40 -040012dayjs.extend(isToday)
13dayjs.extend(isYesterday)
14
15export const MessageCall = (props) => {
16 return (
17 <Stack
18 alignItems="center"
19 >
20 "Appel"
21 </Stack>
22 )
Larbi Gharibe9af9732021-03-31 15:08:01 +010023}
24
idillonbef18a52022-09-01 01:51:40 -040025export const MessageInitial = (props) => {
idillon5815c732022-09-16 13:54:45 -040026 const { t } = useTranslation()
idillonbef18a52022-09-01 01:51:40 -040027 return (
28 <Stack
29 alignItems="center"
30 >
idillon5815c732022-09-16 13:54:45 -040031 {t("message_swarm_created")}
idillonbef18a52022-09-01 01:51:40 -040032 </Stack>
33 )
34}
35
36export const MessageDataTransfer = (props) => {
37 return (
38 <MessageBubble
39 backgroundColor={"#E5E5E5"}
40 position={props.position}
41 isFirstOfGroup={props.isFirstOfGroup}
42 isLastOfGroup={props.isLastOfGroup}
43 >
44 "data-transfer"
45 </MessageBubble>
46 )
47}
48
49export const MessageMember = (props) => {
idillon5815c732022-09-16 13:54:45 -040050 const { t } = useTranslation()
idillonbef18a52022-09-01 01:51:40 -040051 return (
52 <Stack
53 alignItems="center"
54 >
55 <Chip
56 sx={{
57 width: "fit-content",
58 }}
idillon5815c732022-09-16 13:54:45 -040059 label={t("message_user_joined", {user: props.message.author})}
idillonbef18a52022-09-01 01:51:40 -040060 />
61 </Stack>
62 )
63}
64
65export const MessageMerge = (props) => {
66 return (
67 <Stack
68 alignItems="center"
69 >
70 "merge"
71 </Stack>
72 )
73}
74
75export const MessageText = (props) => {
76 return (
77 <MessageBubble
78 backgroundColor={props.bubbleColor}
79 position={props.position}
80 isFirstOfGroup={props.isFirstOfGroup}
81 isLastOfGroup={props.isLastOfGroup}
82 >
idillon89720a82022-09-06 18:47:05 -040083 <Typography variant="body1" color={props.textColor} textAlign={props.position}>
idillonbef18a52022-09-01 01:51:40 -040084 {props.message.body}
85 </Typography>
86 </MessageBubble>
87 )
88}
89
90export const MessageDate = ({time}) => {
91 let textDate
92
93 if (time.isToday()) {
94 textDate = "Today"
95 }
96 else if (time.isYesterday()) {
97 textDate = "Yesterday"
98 }
99 else {
100 const date = time.date().toString().padStart(2,'0')
101 const month = (time.month()+1).toString().padStart(2,'0')
102 textDate = `${date}/${month}/${time.year()}`
103 }
104
105 return (
idillon04245a12022-09-01 11:12:17 -0400106 <Box marginTop="30px" >
107 <Divider
108 sx={{
109 ".MuiDivider-wrapper": {
110 margin: 0,
111 padding: 0,
112 },
113 "&::before": {
114 borderTop: "1px solid #E5E5E5",
115 },
116 "&::after": {
117 borderTop: "1px solid #E5E5E5",
118 },
119 }}
120 >
121 <Typography
122 variant="caption"
123 fontWeight={700}
124 border="1px solid #E5E5E5"
125 borderRadius="5px"
126 padding="10px 16px"
127 >
128 {textDate}
129 </Typography>
idillonbef18a52022-09-01 01:51:40 -0400130 </Divider>
131 </Box>
132 )
133}
134
135export const MessageTime = ({time, hasDateOnTop}) => {
136 const hour = time.hour().toString().padStart(2,'0')
137 const minute = time.minute().toString().padStart(2,'0')
138 const textTime = `${hour}:${minute}`
139
140 return (
141 <Stack
142 direction="row"
143 justifyContent="center"
144 margin="30px"
145 marginTop={hasDateOnTop ? "20px" : "30px"}
146 >
147 <Typography
148 variant="caption"
149 color="#A7A7A7"
150 fontWeight={700}
151 >
152 {textTime}
153 </Typography>
154 </Stack>
155 )
156}
157
158export const MessageBubblesGroup = (props) => {
idillon89720a82022-09-06 18:47:05 -0400159 const isUser = false // should access user from the store
idillonbef18a52022-09-01 01:51:40 -0400160 const position = isUser ? "end" : "start"
161 const bubbleColor = isUser ? "#005699" : "#E5E5E5"
162 const textColor = isUser ? "white" : "black"
163
164 return (
idillon89720a82022-09-06 18:47:05 -0400165 <Stack // Row for a group of message bubbles with the user's infos
idillonbef18a52022-09-01 01:51:40 -0400166 direction="row"
167 justifyContent={position}
idillon89720a82022-09-06 18:47:05 -0400168 alignItems="end"
169 spacing="10px"
idillonbef18a52022-09-01 01:51:40 -0400170 >
idillon89720a82022-09-06 18:47:05 -0400171 {
172 !isUser
173 &&
174 <ConversationAvatar
175 displayName="TempDisplayName"
176 sx={{ width: "22px", height: "22px", fontSize: "15px" }}
177 />
178 }
179 <Stack // Container to align the bubbles to the same side of a row
idillonbef18a52022-09-01 01:51:40 -0400180 width="66.66%"
idillonbef18a52022-09-01 01:51:40 -0400181 alignItems={position}
182 >
183 <ParticipantName
184 name={props.messages[0]?.author}
185 position={position}
186 />
idillon89720a82022-09-06 18:47:05 -0400187 <Stack // Container for a group of message bubbles
idillonbef18a52022-09-01 01:51:40 -0400188 spacing="6px"
189 alignItems={position}
190 direction="column-reverse"
191 >
192 {props.messages.map(
193 (message, index) => {
194 let Component
195 switch (message.type) {
196 case "text/plain":
197 Component = MessageText
198 break
199 case "application/data-transfer+json":
200 Component = MessageDataTransfer
201 break
202 }
203 return (
idillon89720a82022-09-06 18:47:05 -0400204 <Component // Single message
idillonbef18a52022-09-01 01:51:40 -0400205 key={message.id}
206 message={message}
207 textColor={textColor}
208 position={position}
209 bubbleColor={bubbleColor}
210 isFirstOfGroup={index == props.messages.length-1}
211 isLastOfGroup={index == 0}
212 />
213 )
214 }
215 )}
216 </Stack>
217 </Stack>
218 </Stack>
219 )
220}
221
idillon927b7592022-09-15 12:56:45 -0400222const MessageTooltip = styled(({ className, ...props }) => {
223 const [open, setOpen] = useState(false)
224 const emojis = ["😎", "😄", "😍"] // Should be last three used emojis
225 const additionalOptions = [
226 {
227 Icon: TwoSheetsIcon,
228 text: "Copy",
229 action: () => {},
230 },
231 {
232 Icon: OppositeArrowsIcon,
233 text: "Transfer",
234 action: () => {},
235 },
236 {
237 Icon: TrashBinIcon,
238 text: "Delete message",
239 action: () => {},
240 },
241 ]
242
243 const toggleMoreMenu = useCallback(
244 () => setOpen(open => !open),
245 [setOpen]
246 )
247
248 const onClose = useCallback(
249 () => {
250 setOpen(false)
251 },
252 [setOpen]
253 )
254
255 return (
256 <Tooltip
257 {...props}
258 classes={{ tooltip: className }} // Required for styles. Don't know why
259 placement={props.position == "start" ? "right-start" : "left-start"}
260 PopperProps={{
261 modifiers: [
262 {
263 name: "offset",
264 options: {
265 offset: [-2, -30],
266 },
267 },
268 ],
269 }}
270 onClose={onClose}
271 title={
272 <Stack> {/* Whole tooltip's content */}
273 <Stack // Main options
274 direction="row"
275 spacing="16px"
276 >
277 {
278 emojis.map(
279 emoji => <EmojiButton key={emoji} emoji={emoji}/>
280 )
281 }
282 <ReplyMessageButton/>
283 <MoreButton
284 onClick={toggleMoreMenu}
285 />
286 </Stack>
287 {open && // Additional menu options
288 <>
289 <Divider sx={{paddingTop:"16px"}}/>
290 <List sx={{padding: 0, paddingTop: "8px", marginBottom: "-8px"}}>
291 {
292 additionalOptions.map(
293 (option) => (
294 <ListItemButton
295 key={option.text}
296 sx={{
297 padding: "8px",
298 }}
299 >
300 <Stack // Could not find proper way to set spacing between ListItemIcon and ListItemText
301 direction="row"
302 spacing="16px"
303 >
304 <option.Icon
305 sx={{
306 height: "16px",
307 margin: 0,
308 color: theme => theme.palette.primary.dark,
309 }}
310 />
311 <ListItemText
312 primary={option.text}
313 primaryTypographyProps={{
314 fontSize: "12px",
315 lineHeight: "16px",
316 }}
317 sx={{
318 height: "16px",
319 margin: 0,
320 }}
321 />
322 </Stack>
323 </ListItemButton>
324 )
325 )
326 }
327 </List>
328 </>
329 }
330 </Stack>
331 }
332 />
333 )
334})(({ theme, position }) => {
335 const largeRadius = "20px"
336 const smallRadius = "5px"
337 return {
338 backgroundColor: "white",
339 padding: "16px",
340 boxShadow: "3px 3px 7px #00000029",
341 borderRadius: largeRadius,
342 borderStartStartRadius: position == "start" ? smallRadius : largeRadius,
343 borderStartEndRadius: position == "end" ? smallRadius : largeRadius,
344 }
345});
346
idillonbef18a52022-09-01 01:51:40 -0400347const MessageBubble = (props) => {
348 const largeRadius = "20px"
349 const smallRadius = "5px"
Adrien Béraud023f7cf2022-09-18 14:57:53 -0400350 const radius = useMemo(() => {
idillonbef18a52022-09-01 01:51:40 -0400351 if (props.position == "start") {
352 return {
353 borderStartStartRadius: props.isFirstOfGroup ? largeRadius : smallRadius,
354 borderStartEndRadius: largeRadius,
355 borderEndStartRadius: props.isLastOfGroup ? largeRadius : smallRadius,
356 borderEndEndRadius: largeRadius,
357 }
358 }
359 return {
360 borderStartStartRadius: largeRadius,
361 borderStartEndRadius: props.isFirstOfGroup ? largeRadius : smallRadius,
362 borderEndStartRadius: largeRadius,
363 borderEndEndRadius: props.isLastOfGroup ? largeRadius : smallRadius,
364 }
365 }, [props.isFirstOfGroup, props.isLastOfGroup, props.position])
366
367 return (
idillon927b7592022-09-15 12:56:45 -0400368 <MessageTooltip
369 position={props.position}
idillonbef18a52022-09-01 01:51:40 -0400370 >
idillon927b7592022-09-15 12:56:45 -0400371 <Box
372 sx={{
373 width: "fit-content",
374 backgroundColor: props.backgroundColor,
375 padding: "16px",
376 ...radius,
377 }}
378 >
379 {props.children}
380 </Box>
381 </MessageTooltip>
idillonbef18a52022-09-01 01:51:40 -0400382 )
383}
384
385const ParticipantName = (props) => {
386 return (
387 <Box
388 marginBottom="6px"
389 marginLeft="16px"
390 marginRight="16px"
391 >
392 <Typography
393 variant="caption"
394 color="#A7A7A7"
395 fontWeight={700}
396 >
397 {props.name}
398 </Typography>
399 </Box>
400 )
401}