react-garden
Version:
React + TypeScript + ThreeJS app using Material UI on NextJS, Apollo Client, GraphQL + WordPress REST APIs, for ThreeD web development.. a part of the threed.ai code family.
647 lines (615 loc) • 24.6 kB
JavaScript
// ** React Imports
import { Fragment, useState } from 'react'
// ** MUI Imports
import List from '@mui/material/List'
import Menu from '@mui/material/Menu'
import Avatar from '@mui/material/Avatar'
import Divider from '@mui/material/Divider'
import MenuItem from '@mui/material/MenuItem'
import ListItem from '@mui/material/ListItem'
import { styled } from '@mui/material/styles'
import IconButton from '@mui/material/IconButton'
import Box from '@mui/material/Box'
import Typography from '@mui/material/Typography'
import ListItemIcon from '@mui/material/ListItemIcon'
// ** Icons Import
import Circle from 'mdi-material-ui/Circle'
import Attachment from 'mdi-material-ui/Attachment'
import StarOutline from 'mdi-material-ui/StarOutline'
import ChevronLeft from 'mdi-material-ui/ChevronLeft'
import ChevronRight from 'mdi-material-ui/ChevronRight'
import ShareOutline from 'mdi-material-ui/ShareOutline'
import LabelOutline from 'mdi-material-ui/LabelOutline'
import DotsVertical from 'mdi-material-ui/DotsVertical'
import ReplyOutline from 'mdi-material-ui/ReplyOutline'
import EmailOutline from 'mdi-material-ui/EmailOutline'
import DeleteOutline from 'mdi-material-ui/DeleteOutline'
import FolderOutline from 'mdi-material-ui/FolderOutline'
import ArrowExpandVertical from 'mdi-material-ui/ArrowExpandVertical'
import ArrowCollapseVertical from 'mdi-material-ui/ArrowCollapseVertical'
// ** Third Party Imports
import PerfectScrollbar from 'react-perfect-scrollbar'
// ** Hooks
import { useSettings } from '~/@core/hooks/useSettings'
// ** Custom Components Imports
import Sidebar from '~/@core/components/sidebar'
import CustomChip from '~/@core/components/mui/chip'
const HiddenReplyBack = styled(Box)(({ theme }) => ({
height: 11,
width: '90%',
opacity: 0.5,
borderWidth: 1,
borderBottom: 0,
display: 'block',
marginLeft: 'auto',
marginRight: 'auto',
borderStyle: 'solid',
borderTopLeftRadius: theme.shape.borderRadius,
borderTopRightRadius: theme.shape.borderRadius,
backgroundColor: theme.palette.background.paper,
borderColor: `rgba(${theme.palette.customColors.main}, 0.12)`
}))
const HiddenReplyFront = styled(Box)(({ theme }) => ({
height: 12,
width: '95%',
opacity: 0.75,
borderWidth: 1,
borderBottom: 0,
display: 'block',
marginLeft: 'auto',
marginRight: 'auto',
borderStyle: 'solid',
borderTopLeftRadius: theme.shape.borderRadius,
borderTopRightRadius: theme.shape.borderRadius,
backgroundColor: theme.palette.background.paper,
borderColor: `rgba(${theme.palette.customColors.main}, 0.12)`
}))
const MailCardMenu = () => {
const [mailMenuAnchorEl, setMailMenuAnchorEl] = useState(null)
const openMailMenu = Boolean(mailMenuAnchorEl)
const handleMailMenuClick = event => {
setMailMenuAnchorEl(event.currentTarget)
}
const handleMailMenuClose = () => {
setMailMenuAnchorEl(null)
}
return (
<>
<IconButton size='small' onClick={handleMailMenuClick}>
<DotsVertical sx={{ fontSize: '1.375rem' }} />
</IconButton>
<Menu
anchorEl={mailMenuAnchorEl}
open={openMailMenu}
onClose={handleMailMenuClose}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'right'
}}
transformOrigin={{
vertical: 'top',
horizontal: 'right'
}}
>
<MenuItem>
<ShareOutline fontSize='small' sx={{ mr: 2 }} />
Reply
</MenuItem>
<MenuItem>
<ReplyOutline fontSize='small' sx={{ mr: 2 }} />
Forward
</MenuItem>
</Menu>
</>
)
}
const MailDetails = props => {
// ** Props
const {
mail,
hidden,
folders,
dispatch,
direction,
updateMail,
foldersObj,
labelColors,
routeParams,
paginateMail,
handleStarMail,
mailDetailsOpen,
handleLabelUpdate,
handleFolderUpdate,
setMailDetailsOpen
} = props
// ** State
const [showReplies, setShowReplies] = useState(false)
const [labelAnchorEl, setLabelAnchorEl] = useState(null)
const [folderAnchorEl, setFolderAnchorEl] = useState(null)
// ** Hook
const { settings } = useSettings()
// ** Vars
const openLabelMenu = Boolean(labelAnchorEl)
const openFolderMenu = Boolean(folderAnchorEl)
const handleMoveToTrash = () => {
dispatch(updateMail({ emailIds: [mail.id], dataToUpdate: { folder: 'trash' } }))
setMailDetailsOpen(false)
}
const handleLabelMenuClick = event => {
setLabelAnchorEl(event.currentTarget)
}
const handleLabelMenuClose = () => {
setLabelAnchorEl(null)
}
const handleFolderMenuClick = event => {
setFolderAnchorEl(event.currentTarget)
}
const handleFolderMenuClose = () => {
setFolderAnchorEl(null)
setMailDetailsOpen(false)
}
const handleReadMail = () => {
dispatch(updateMail({ emailIds: [mail.id], dataToUpdate: { isRead: false } }))
setMailDetailsOpen(false)
}
const renderLabelsMenu = () => {
return Object.entries(labelColors).map(([key, value]) => {
return (
<MenuItem
key={key}
sx={{ display: 'flex', alignItems: 'center' }}
onClick={() => {
handleLabelUpdate([mail.id], key)
handleLabelMenuClose()
}}
>
<Circle sx={{ mr: 2, fontSize: '0.75rem', color: `${value}.main` }} />
<Typography sx={{ textTransform: 'capitalize' }}>{key}</Typography>
</MenuItem>
)
})
}
const renderFoldersMenu = () => {
if (routeParams && routeParams.folder && !routeParams.label && foldersObj[routeParams.folder]) {
return foldersObj[routeParams.folder].map(folder => {
return (
<MenuItem
key={folder.name}
sx={{ display: 'flex', alignItems: 'center' }}
onClick={() => {
handleFolderUpdate(mail.id, folder.name)
handleFolderMenuClose()
}}
>
{folder.icon}
<Typography sx={{ textTransform: 'capitalize' }}>{folder.name}</Typography>
</MenuItem>
)
})
} else if (routeParams && routeParams.label) {
return folders.map(folder => {
return (
<MenuItem
key={folder.name}
sx={{ display: 'flex', alignItems: 'center' }}
onClick={() => {
handleFolderUpdate(mail.id, folder.name)
handleFolderMenuClose()
}}
>
{folder.icon}
<Typography sx={{ textTransform: 'capitalize' }}>{folder.name}</Typography>
</MenuItem>
)
})
} else {
return foldersObj['inbox'].map(folder => {
return (
<MenuItem
key={folder.name}
sx={{ display: 'flex', alignItems: 'center' }}
onClick={() => {
handleFolderUpdate(mail.id, folder.name)
handleFolderMenuClose()
}}
>
{folder.icon}
<Typography sx={{ textTransform: 'capitalize' }}>{folder.name}</Typography>
</MenuItem>
)
})
}
}
const PrevMailIcon = direction === 'rtl' ? ChevronRight : ChevronLeft
const NextMailIcon = direction === 'rtl' ? ChevronLeft : ChevronRight
const GoBackIcon = PrevMailIcon
const ScrollWrapper = ({ children }) => {
if (hidden) {
return <Box sx={{ height: '100%', overflowY: 'auto', overflowX: 'hidden' }}>{children}</Box>
} else {
return <PerfectScrollbar options={{ wheelPropagation: false }}>{children}</PerfectScrollbar>
}
}
return (
<Sidebar
hideBackdrop
direction='right'
show={mailDetailsOpen}
sx={{ zIndex: 1, width: '100%', overflow: 'hidden' }}
onClose={() => {
setMailDetailsOpen(false)
setShowReplies(false)
}}
>
{mail ? (
<>
<Box
sx={{
px: 2.6,
py: [2.25, 3],
backgroundColor: 'background.paper',
borderBottom: theme => `1px solid ${theme.palette.divider}`
}}
>
<Box sx={{ display: 'flex', alignItems: ['flex-start', 'center'], justifyContent: 'space-between' }}>
<Box
sx={{
display: 'flex',
overflow: 'hidden',
alignItems: 'center',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis'
}}
>
<IconButton
size='small'
sx={{ mr: 3.5 }}
onClick={() => {
setMailDetailsOpen(false)
setShowReplies(false)
}}
>
<GoBackIcon sx={{ fontSize: '2rem' }} />
</IconButton>
<Box
sx={{
display: 'flex',
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
flexDirection: ['column', 'row']
}}
>
<Typography noWrap sx={{ mr: 2, fontWeight: 500 }}>
{mail.subject}
</Typography>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
{mail.labels && mail.labels.length
? mail.labels.map(label => {
return (
<CustomChip
key={label}
size='small'
skin='light'
label={label}
color={labelColors[label]}
sx={{ textTransform: 'capitalize', '&:not(:last-of-type)': { mr: 2 } }}
/>
)
})
: null}
</Box>
</Box>
</Box>
<Box sx={{ display: 'flex' }}>
<IconButton
size='small'
disabled={!mail.hasPreviousMail}
sx={{ color: mail.hasPreviousMail ? 'text.primary' : 'text.secondary' }}
onClick={() => dispatch(paginateMail({ dir: 'previous', emailId: mail.id }))}
>
<PrevMailIcon />
</IconButton>
<IconButton
size='small'
disabled={!mail.hasNextMail}
sx={{ color: mail.hasNextMail ? 'text.primary' : 'text.secondary' }}
onClick={() => dispatch(paginateMail({ dir: 'next', emailId: mail.id }))}
>
<NextMailIcon />
</IconButton>
</Box>
</Box>
</Box>
<Box
sx={{
backgroundColor: 'background.paper',
p: theme => theme.spacing(3, 2, 3, 3),
borderBottom: theme => `1px solid ${theme.palette.divider}`
}}
>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
{routeParams && routeParams.folder !== 'trash' ? (
<IconButton size='small' onClick={handleMoveToTrash}>
<DeleteOutline sx={{ fontSize: '1.375rem' }} />
</IconButton>
) : null}
<IconButton size='small' onClick={handleReadMail}>
<EmailOutline sx={{ fontSize: '1.375rem' }} />
</IconButton>
<IconButton size='small' onClick={handleFolderMenuClick}>
<FolderOutline sx={{ fontSize: '1.375rem' }} />
</IconButton>
<Menu
open={openLabelMenu}
anchorEl={labelAnchorEl}
onClose={handleLabelMenuClose}
PaperProps={{ style: { minWidth: '9rem' } }}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left'
}}
transformOrigin={{
vertical: 'top',
horizontal: 'left'
}}
>
{renderLabelsMenu()}
</Menu>
<IconButton size='small' onClick={handleLabelMenuClick}>
<LabelOutline sx={{ fontSize: '1.375rem' }} />
</IconButton>
<Menu
open={openFolderMenu}
anchorEl={folderAnchorEl}
onClose={() => setFolderAnchorEl(null)}
PaperProps={{ style: { minWidth: '9rem' } }}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left'
}}
transformOrigin={{
vertical: 'top',
horizontal: 'left'
}}
>
{renderFoldersMenu()}
</Menu>
</Box>
<Box>
<IconButton
size='small'
onClick={e => handleStarMail(e, mail.id, !mail.isStarred)}
sx={{ ...(mail.isStarred ? { color: 'warning.main' } : {}) }}
>
<StarOutline sx={{ fontSize: '1.375rem' }} />
</IconButton>
{mail.replies.length ? (
<IconButton size='small' onClick={() => (showReplies ? setShowReplies(false) : setShowReplies(true))}>
{showReplies ? (
<ArrowCollapseVertical sx={{ fontSize: '1.375rem' }} />
) : (
<ArrowExpandVertical sx={{ fontSize: '1.375rem' }} />
)}
</IconButton>
) : null}
<IconButton size='small'>
<DotsVertical sx={{ fontSize: '1.375rem' }} />
</IconButton>
</Box>
</Box>
</Box>
<Box sx={{ height: 'calc(100% - 7.75rem)', backgroundColor: theme => theme.palette.action.hover }}>
<ScrollWrapper>
<Box
sx={{
py: 4,
px: 5,
width: '100%',
display: 'flex',
alignItems: 'center',
flexDirection: 'column',
justifyContent: 'center'
}}
>
{mail.replies.length && !showReplies ? (
<Typography onClick={() => setShowReplies(true)} sx={{ mt: 1.5, mb: 5, cursor: 'pointer' }}>
{mail.replies.length} Earlier Messages
</Typography>
) : null}
{showReplies
? mail.replies.map((reply, index) => {
return (
<Box
key={index}
sx={{
mb: 4,
boxShadow: 6,
width: '100%',
borderRadius: 1,
backgroundColor: 'background.paper',
border: theme => `1px solid ${theme.palette.divider}`
}}
>
<Box sx={{ p: 5 }}>
<Box
sx={{
display: 'flex',
flexWrap: 'wrap',
alignItems: 'center',
justifyContent: 'space-between'
}}
>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Avatar
alt={reply.from.name}
src={reply.from.avatar}
sx={{ width: '2.375rem', height: '2.375rem', mr: 3 }}
/>
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
<Typography sx={{ fontWeight: 500 }}>{reply.from.name}</Typography>
<Typography variant='body2'>{reply.from.email}</Typography>
</Box>
</Box>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Typography variant='caption' sx={{ mr: 3 }}>
{new Date(reply.time).toDateString()}{' '}
{new Date(reply.time).toLocaleTimeString('en-US', {
hour: '2-digit',
minute: '2-digit',
hour12: true
})}
</Typography>
{mail.attachments.length ? (
<IconButton size='small'>
<Attachment sx={{ fontSize: '1.375rem' }} />
</IconButton>
) : null}
<IconButton size='small'>
<DotsVertical sx={{ fontSize: '1.375rem' }} />
</IconButton>
</Box>
</Box>
</Box>
<Divider sx={{ m: 0 }} />
<Box sx={{ p: 5, pt: 0 }}>
<Box dangerouslySetInnerHTML={{ __html: reply.message }} />
</Box>
{reply.attachments.length ? (
<>
<Divider sx={{ m: 0 }} />
<Box sx={{ p: 5 }}>
<Typography variant='body2'>Attachments</Typography>
<List>
{reply.attachments.map(item => {
return (
<ListItem disableGutters key={item.fileName}>
<ListItemIcon>
<img src={item.thumbnail} alt={item.fileName} width='24' height='24' />
</ListItemIcon>
<Typography variant='caption'>{item.fileName}</Typography>
</ListItem>
)
})}
</List>
</Box>
</>
) : null}
</Box>
)
})
: null}
{mail.replies.length && !showReplies ? (
<>
<HiddenReplyBack sx={{ cursor: 'pointer' }} onClick={() => setShowReplies(true)} />
<HiddenReplyFront sx={{ cursor: 'pointer' }} onClick={() => setShowReplies(true)} />
</>
) : null}
<Box
sx={{
mb: 4,
width: '100%',
borderRadius: 1,
overflow: 'visible',
position: 'relative',
backgroundColor: 'background.paper',
boxShadow: settings.skin === 'bordered' ? 0 : 6,
border: theme => `1px solid ${theme.palette.divider}`
}}
>
<Box sx={{ p: 5 }}>
<Box
sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', flexWrap: 'wrap' }}
>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Avatar
alt={mail.from.name}
src={mail.from.avatar}
sx={{ width: '2.375rem', height: '2.375rem', mr: 3 }}
/>
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
<Typography sx={{ fontWeight: 500 }}>{mail.from.name}</Typography>
<Typography variant='body2'>{mail.from.email}</Typography>
</Box>
</Box>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Typography variant='caption' sx={{ mr: 3 }}>
{new Date(mail.time).toDateString()}{' '}
{new Date(mail.time).toLocaleTimeString('en-US', {
hour: '2-digit',
minute: '2-digit',
hour12: true
})}
</Typography>
{mail.attachments.length ? (
<IconButton size='small'>
<Attachment sx={{ fontSize: '1.375rem' }} />
</IconButton>
) : null}
<MailCardMenu />
</Box>
</Box>
</Box>
<Divider sx={{ m: 0 }} />
<Box sx={{ p: 5, pt: 0 }}>
<Box dangerouslySetInnerHTML={{ __html: mail.message }} />
</Box>
{mail.attachments.length ? (
<>
<Divider sx={{ m: 0 }} />
<Box sx={{ p: 5 }}>
<Typography variant='body2'>Attachments</Typography>
<List>
{mail.attachments.map(item => {
return (
<ListItem disableGutters key={item.fileName}>
<ListItemIcon>
<img src={item.thumbnail} alt={item.fileName} width='24' height='24' />
</ListItemIcon>
<Typography variant='caption'>{item.fileName}</Typography>
</ListItem>
)
})}
</List>
</Box>
</>
) : null}
</Box>
<Box
sx={{
p: 5,
width: '100%',
borderRadius: 1,
border: '1px solid',
borderColor: 'divider',
backgroundColor: 'background.paper',
boxShadow: settings.skin === 'bordered' ? 0 : 6
}}
>
<Typography sx={{ fontWeight: 500 }}>
Click here to{' '}
<Typography
component='span'
sx={{ cursor: 'pointer', color: 'primary.main', fontWeight: 'inherit' }}
>
Reply
</Typography>{' '}
or{' '}
<Typography
component='span'
sx={{ cursor: 'pointer', color: 'primary.main', fontWeight: 'inherit' }}
>
Forward
</Typography>
</Typography>
</Box>
</Box>
</ScrollWrapper>
</Box>
</>
) : null}
</Sidebar>
)
}
export default MailDetails