242 lines
7.2 KiB
TypeScript
242 lines
7.2 KiB
TypeScript
// React Imports
|
|
import { useRef, useState, useEffect } from 'react'
|
|
import type { FormEvent, KeyboardEvent, RefObject, MouseEvent } from 'react'
|
|
|
|
// MUI Imports
|
|
import TextField from '@mui/material/TextField'
|
|
import Button from '@mui/material/Button'
|
|
import IconButton from '@mui/material/IconButton'
|
|
import Popper from '@mui/material/Popper'
|
|
import Fade from '@mui/material/Fade'
|
|
import Paper from '@mui/material/Paper'
|
|
import ClickAwayListener from '@mui/material/ClickAwayListener'
|
|
import Menu from '@mui/material/Menu'
|
|
import MenuItem from '@mui/material/MenuItem'
|
|
|
|
// Third-party Imports
|
|
import Picker from '@emoji-mart/react'
|
|
import data from '@emoji-mart/data'
|
|
|
|
// Type Imports
|
|
import type { ContactType } from '@/types/apps/chatTypes'
|
|
import type { AppDispatch } from '@/redux-store'
|
|
|
|
// Slice Imports
|
|
import { sendMsg } from '@/redux-store/slices/chat'
|
|
|
|
// Component Imports
|
|
import CustomIconButton from '@core/components/mui/IconButton'
|
|
|
|
type Props = {
|
|
dispatch: AppDispatch
|
|
activeUser: ContactType
|
|
isBelowSmScreen: boolean
|
|
messageInputRef: RefObject<HTMLDivElement>
|
|
}
|
|
|
|
// Emoji Picker Component for selecting emojis
|
|
const EmojiPicker = ({
|
|
onChange,
|
|
isBelowSmScreen,
|
|
openEmojiPicker,
|
|
setOpenEmojiPicker,
|
|
anchorRef
|
|
}: {
|
|
onChange: (value: string) => void
|
|
isBelowSmScreen: boolean
|
|
openEmojiPicker: boolean
|
|
setOpenEmojiPicker: (value: boolean | ((prevVar: boolean) => boolean)) => void
|
|
anchorRef: RefObject<HTMLButtonElement>
|
|
}) => {
|
|
return (
|
|
<>
|
|
<Popper
|
|
open={openEmojiPicker}
|
|
transition
|
|
disablePortal
|
|
placement='top-start'
|
|
className='z-[12]'
|
|
anchorEl={anchorRef.current}
|
|
>
|
|
{({ TransitionProps, placement }) => (
|
|
<Fade {...TransitionProps} style={{ transformOrigin: placement === 'top-start' ? 'right top' : 'left top' }}>
|
|
<Paper>
|
|
<ClickAwayListener onClickAway={() => setOpenEmojiPicker(false)}>
|
|
<span>
|
|
<Picker
|
|
emojiSize={18}
|
|
theme='light'
|
|
data={data}
|
|
maxFrequentRows={1}
|
|
onEmojiSelect={(emoji: any) => {
|
|
onChange(emoji.native)
|
|
setOpenEmojiPicker(false)
|
|
}}
|
|
{...(isBelowSmScreen && { perLine: 8 })}
|
|
/>
|
|
</span>
|
|
</ClickAwayListener>
|
|
</Paper>
|
|
</Fade>
|
|
)}
|
|
</Popper>
|
|
</>
|
|
)
|
|
}
|
|
|
|
const SendMsgForm = ({ dispatch, activeUser, isBelowSmScreen, messageInputRef }: Props) => {
|
|
// States
|
|
const [msg, setMsg] = useState('')
|
|
const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null)
|
|
const [openEmojiPicker, setOpenEmojiPicker] = useState(false)
|
|
|
|
// Refs
|
|
const anchorRef = useRef<HTMLButtonElement>(null)
|
|
|
|
// Vars
|
|
const open = Boolean(anchorEl)
|
|
|
|
const handleToggle = () => {
|
|
setOpenEmojiPicker(prevOpen => !prevOpen)
|
|
}
|
|
|
|
const handleClick = (event: MouseEvent<HTMLButtonElement>) => {
|
|
setAnchorEl(prev => (prev ? null : event.currentTarget))
|
|
}
|
|
|
|
const handleClose = () => {
|
|
setAnchorEl(null)
|
|
}
|
|
|
|
const handleSendMsg = (event: FormEvent | KeyboardEvent, msg: string) => {
|
|
event.preventDefault()
|
|
|
|
if (msg.trim() !== '') {
|
|
dispatch(sendMsg({ msg }))
|
|
setMsg('')
|
|
}
|
|
}
|
|
|
|
const handleInputEndAdornment = () => {
|
|
return (
|
|
<div className='flex items-center gap-1'>
|
|
{isBelowSmScreen ? (
|
|
<>
|
|
<IconButton
|
|
id='option-menu'
|
|
aria-haspopup='true'
|
|
{...(open && { 'aria-expanded': true, 'aria-controls': 'share-menu' })}
|
|
onClick={handleClick}
|
|
ref={anchorRef}
|
|
>
|
|
<i className='tabler-dots-vertical text-textPrimary' />
|
|
</IconButton>
|
|
<Menu anchorEl={anchorEl} open={open} onClose={handleClose}>
|
|
<MenuItem
|
|
onClick={() => {
|
|
handleToggle()
|
|
handleClose()
|
|
}}
|
|
>
|
|
<i className='tabler-mood-smile' />
|
|
</MenuItem>
|
|
<MenuItem onClick={handleClose}>
|
|
<i className='tabler-microphone' />
|
|
</MenuItem>
|
|
<MenuItem onClick={handleClose} className='p-0'>
|
|
<label htmlFor='upload-img' className='plb-2 pli-4'>
|
|
<i className='tabler-paperclip' />
|
|
<input hidden type='file' id='upload-img' />
|
|
</label>
|
|
</MenuItem>
|
|
</Menu>
|
|
<EmojiPicker
|
|
anchorRef={anchorRef}
|
|
openEmojiPicker={openEmojiPicker}
|
|
setOpenEmojiPicker={setOpenEmojiPicker}
|
|
isBelowSmScreen={isBelowSmScreen}
|
|
onChange={value => {
|
|
setMsg(msg + value)
|
|
|
|
if (messageInputRef.current) {
|
|
messageInputRef.current.focus()
|
|
}
|
|
}}
|
|
/>
|
|
</>
|
|
) : (
|
|
<>
|
|
<IconButton ref={anchorRef} onClick={handleToggle}>
|
|
<i className='tabler-mood-smile cursor-pointer text-textPrimary' />
|
|
</IconButton>
|
|
<EmojiPicker
|
|
anchorRef={anchorRef}
|
|
openEmojiPicker={openEmojiPicker}
|
|
setOpenEmojiPicker={setOpenEmojiPicker}
|
|
isBelowSmScreen={isBelowSmScreen}
|
|
onChange={value => {
|
|
setMsg(msg + value)
|
|
|
|
if (messageInputRef.current) {
|
|
messageInputRef.current.focus()
|
|
}
|
|
}}
|
|
/>
|
|
<IconButton>
|
|
<i className='tabler-microphone text-textPrimary' />
|
|
</IconButton>
|
|
<IconButton component='label' htmlFor='upload-img'>
|
|
<i className='tabler-paperclip text-textPrimary' />
|
|
<input hidden type='file' id='upload-img' />
|
|
</IconButton>
|
|
</>
|
|
)}
|
|
{isBelowSmScreen ? (
|
|
<CustomIconButton variant='contained' color='primary' type='submit'>
|
|
<i className='tabler-send' />
|
|
</CustomIconButton>
|
|
) : (
|
|
<Button variant='contained' color='primary' type='submit' endIcon={<i className='tabler-send' />}>
|
|
Send
|
|
</Button>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
useEffect(() => {
|
|
setMsg('')
|
|
}, [activeUser.id])
|
|
|
|
return (
|
|
<form autoComplete='off' onSubmit={event => handleSendMsg(event, msg)}>
|
|
<TextField
|
|
fullWidth
|
|
multiline
|
|
maxRows={4}
|
|
placeholder='Type a message'
|
|
value={msg}
|
|
className='p-6'
|
|
onChange={e => setMsg(e.target.value)}
|
|
sx={{
|
|
'& fieldset': { border: '0' },
|
|
'& .MuiOutlinedInput-root': {
|
|
background: 'var(--mui-palette-background-paper)',
|
|
boxShadow: 'var(--mui-customShadows-xs) !important'
|
|
}
|
|
}}
|
|
onKeyDown={(e: KeyboardEvent<HTMLDivElement>) => {
|
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
handleSendMsg(e, msg)
|
|
}
|
|
}}
|
|
size='small'
|
|
inputRef={messageInputRef}
|
|
slotProps={{ input: { endAdornment: handleInputEndAdornment() } }}
|
|
/>
|
|
</form>
|
|
)
|
|
}
|
|
|
|
export default SendMsgForm
|