ChatPage.jsx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. import { useEffect, useState} from 'react';
  2. import {Button, Box} from '@mui/material';
  3. import TextareaAutosize from '@mui/material/TextareaAutosize';
  4. import { MessageForm } from './messageForm/MessegaForm';
  5. import { UserInfo } from './userInfo/UserInfo';
  6. import { store } from '../../store';
  7. import { removeToken} from '../../reducers/userDataReducer'
  8. import { useDispatch, useSelector } from 'react-redux';
  9. import {getSocket} from'../../reducers/socketReducer';
  10. import { sendMessage, storeMessage, fileMessage } from '../../reducers/messageReducer';
  11. import { editMessage } from '../../reducers/messageReducer';
  12. import { SwitchButton } from './SwitchButton';
  13. import { MessageEditorMenu } from './MessageEditorMenu.jsx';
  14. import imgBtn from '../../assets/img/gg.png';
  15. import imgBtnPhoto from '../../assets/img/photo.png'
  16. import './chatPage.scss';
  17. import WebcamCapture from './service/webCam/WebcamCapture';
  18. import useSound from 'use-sound';
  19. import getNotif from '../../assets/sendSound.mp3'
  20. export const ChatPage = () => {
  21. const SOCKET_EVENTS = ['allmessages', 'usersOnline', 'allDbUsers']
  22. const dispatch = useDispatch();
  23. const token = useSelector(state => localStorage.getItem('token') || state.userDataReducer.token);
  24. const user = useSelector(state => state.getUserSocketReducer.socketUserData)
  25. const socket = useSelector(state => state.getUserSocketReducer.socket)
  26. const editOldMessage = useSelector(state => state.messageReducer.editMessage)
  27. let showUserInfoBox = useSelector(state => state.messageReducer.showUserInfoBox)// || localStorage.getItem('showBox');
  28. const [message, setMessage] = useState({message: ''});
  29. const [isUserTyping, setUserTyping] = useState([]);
  30. const [isCamActiv, setisCamActiv] = useState(false);
  31. const [showSpinner, setshowSpinner] = useState(false);
  32. const [loadingPercentage, setLoadPercentage] = useState(0)
  33. const isTabletorMobile = (window.screen.width < 730);
  34. const [play] = useSound(getNotif, {volume: 0.005});
  35. const axiosConfig = {
  36. headers: {
  37. "Content-type": "multipart/form-data"
  38. },
  39. onUploadProgress: (progress) => {
  40. const {loaded, total} = progress;
  41. const loadStatus = Math.floor(loaded * 100 / total);
  42. setLoadPercentage(loadStatus)
  43. if(loadStatus == 100) {
  44. setshowSpinner(false)
  45. }
  46. }}
  47. const webcamEventHandler = async () => {
  48. let stream = null;
  49. try {
  50. stream = await navigator.mediaDevices.getUserMedia({ video: { width: 300 }});
  51. if (stream){
  52. setisCamActiv(!isCamActiv)
  53. }
  54. } catch(err) {
  55. console.log(err)
  56. }
  57. setisCamActiv(!isCamActiv) // test camera
  58. }
  59. useEffect(() => {
  60. if(socket) {
  61. socket.on('writing', (data) => {
  62. setUserTyping(data)
  63. setTimeout(() => setUserTyping([]), 500 )
  64. })
  65. }
  66. }, [socket])
  67. useEffect(() => {
  68. if(token){
  69. SOCKET_EVENTS.map(event => dispatch(getSocket(event)))
  70. }
  71. }, [token, editOldMessage, showUserInfoBox])
  72. return (
  73. <div className='rootContainer'>
  74. <Box className = {isTabletorMobile?'mobileRootBox':'rootBox'}>
  75. <Box className={isTabletorMobile?'usersBoxMobile':'usersBox'}
  76. sx = {showUserInfoBox ? {
  77. transform: "translateX(100%)",
  78. display: "none"
  79. }: { }}>
  80. <UserInfo/>
  81. { isTabletorMobile ? <SwitchButton/> : null}
  82. <Button
  83. style={isTabletorMobile ?
  84. {
  85. maxHeight:'20px',
  86. maxWidth: '15px',
  87. fontSize: '10px',
  88. marginLeft: '25px',
  89. marginRight: '10px'}
  90. :{margin:'10px 5px'}}
  91. variant="contained"
  92. onClick={()=> {
  93. localStorage.removeItem('token');
  94. dispatch(removeToken());
  95. socket.disconnect();
  96. }}>
  97. Logout
  98. </Button>
  99. </Box>
  100. <Box className ={isTabletorMobile ? 'rootMessageFormMobile':'rootMessageForm'} >
  101. {isCamActiv ?
  102. <div>
  103. <Button
  104. variant="contained"
  105. component="label"
  106. onClick = {() => webcamEventHandler()}
  107. >
  108. close camera
  109. </Button>
  110. <WebcamCapture />
  111. </div>
  112. :
  113. ""}
  114. <MessageForm/>
  115. {isUserTyping.isTyping && (isUserTyping.userName !== user.userName)? <span> User {isUserTyping.userName} typing..</span> : ""}
  116. <Box
  117. component="form"
  118. onSubmit = {e => {
  119. e.preventDefault()
  120. if (message.message.length){
  121. dispatch(sendMessage({user, socket}))
  122. dispatch(getSocket('allmessages'))
  123. dispatch(editMessage({editMessage: ''}))
  124. setMessage({message: ''})
  125. play()
  126. }
  127. }}
  128. sx={(isTabletorMobile)?{
  129. display: 'flex',
  130. margin: '10px 2px'}
  131. :{
  132. display: 'flex',
  133. margin: '20px 5px'}
  134. }>
  135. <Button
  136. disabled={showSpinner}
  137. variant="contained"
  138. component="label"
  139. sx = {showSpinner?
  140. {
  141. minWidth: 'auto'}
  142. :
  143. {
  144. minWidth: 'auto',
  145. backgroundImage:'url(' + imgBtn + ')' ,
  146. backgroundPosition: 'center',
  147. backgroundRepeat: "no-repeat",
  148. backgroundSize: '20px 20px'
  149. }}
  150. style = {{color: 'green'}}
  151. >
  152. <input
  153. onChange={e =>{
  154. setshowSpinner(true)
  155. dispatch(fileMessage({files: e.target.files, axiosConfig}))
  156. }}
  157. type="file"
  158. multiple
  159. hidden
  160. />
  161. {showSpinner? loadingPercentage : ""}
  162. </Button>
  163. <Button
  164. variant="contained"
  165. component="label"
  166. sx = {{
  167. minWidth: 'auto',
  168. backgroundImage:'url(' + imgBtnPhoto + ')' ,
  169. backgroundPosition: 'center',
  170. backgroundRepeat: "no-repeat",
  171. backgroundSize: '20px 20px'
  172. }}
  173. onClick = {() => webcamEventHandler()}
  174. >
  175. </Button>
  176. <TextareaAutosize
  177. id="outlined-basic"
  178. label="Type a message..."
  179. variant="outlined"
  180. value={message.message}
  181. placeholder='type you message...'
  182. minRows={2}
  183. maxRows={4}
  184. className='textArea'
  185. onKeyPress={(e) => {
  186. if (e.key === "Enter") {
  187. e.preventDefault();
  188. dispatch(sendMessage({user, socket}))
  189. dispatch(getSocket('allmessages'))
  190. dispatch(editMessage({editMessage: ''}))
  191. setMessage({message: ''})
  192. }
  193. }}
  194. onChange={e => {
  195. dispatch(storeMessage({message: e.target.value}))
  196. socket.emit('userWriting');
  197. setMessage({message: e.target.value})}
  198. }
  199. // onFocus={ e => {
  200. // if (isTabletorMobile) {
  201. // e.target.className = 'focus'
  202. // }
  203. // }}
  204. // onBlur={e=> {
  205. // e.target.className = 'blur'
  206. // }}
  207. />
  208. <Button
  209. variant="contained"
  210. type='submit'
  211. disabled={user?.isMutted || !message.message.length}
  212. style={{width: '20%'}}
  213. >
  214. Send
  215. </Button>
  216. </Box>
  217. </Box>
  218. </Box>
  219. </div>
  220. )
  221. }