ChatPage.jsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  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, isPrivatChat, privateMessage} 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. import { PrivateChat } from './privateChat/PrivateChat';
  21. import { PrivatChatHeader } from './privateChat/PrivatChatHeader';
  22. import { socketEvents } from '../../utils/socketsEvents';
  23. import { addNewPrivateMessage } from '../../reducers/socketReducer';
  24. export const ChatPage = () => {
  25. const dispatch = useDispatch();
  26. const token = useSelector(state => localStorage.getItem('token') || state.userDataReducer.token);
  27. const user = useSelector(state => state.getUserSocketReducer.socketUserData)
  28. const socket = useSelector(state => state.getUserSocketReducer.socket)
  29. const editOldMessage = useSelector(state => state.messageReducer.editMessage)
  30. let showUserInfoBox = useSelector(state => state.messageReducer.showUserInfoBox)// || localStorage.getItem('showBox');
  31. const toUser = useSelector(state => state.userDataReducer.toUser)
  32. const chatId = useSelector(state => state.userDataReducer.toUser.socketId)
  33. const isPrivatChat = useSelector(state => state.userDataReducer.isPrivatChat)
  34. const newPrivateMessages = useSelector(state => state.getUserSocketReducer.newPrivateMessages)
  35. const [message, setMessage] = useState({message: ''});
  36. const [isUserTyping, setUserTyping] = useState([]);
  37. const [isCamActiv, setisCamActiv] = useState(false);
  38. const [showSpinner, setshowSpinner] = useState(false);
  39. const [loadingPercentage, setLoadPercentage] = useState(0)
  40. const usersOnline = useSelector(state => state.getUserSocketReducer.usersOnline);
  41. const isTabletorMobile = (window.screen.width < 730);
  42. const isNewMessage = newPrivateMessages.length > 0
  43. const [play] = useSound(getNotif, {volume: 0.005});
  44. const axiosConfig = {
  45. headers: {
  46. "Content-type": "multipart/form-data"
  47. },
  48. onUploadProgress: (progress) => {
  49. const {loaded, total} = progress;
  50. const loadStatus = Math.floor(loaded * 100 / total);
  51. setLoadPercentage(loadStatus)
  52. if(loadStatus == 100) {
  53. setshowSpinner(false)
  54. }
  55. }}
  56. const webcamEventHandler = async () => {
  57. let stream = null;
  58. try {
  59. stream = await navigator.mediaDevices.getUserMedia({ video: { width: 300 }});
  60. if (stream){
  61. setisCamActiv(!isCamActiv)
  62. }
  63. } catch(err) {
  64. console.log(err)
  65. }
  66. setisCamActiv(!isCamActiv) // test camera
  67. }
  68. const sendPrivateMessage = () => {
  69. const toUserSocket = usersOnline.find(user => user._id == toUser._id)|| toUser
  70. const fromUserSocket = usersOnline.find(userInSocket => userInSocket._id == user._id)
  71. ///***need to fix sending own messages to me
  72. // store.dispatch(addNewPrivateMessage({
  73. // fromUser: fromUserSocket,
  74. // message: message.message,
  75. // to: chatId,
  76. // toUser:toUserSocket
  77. // }))
  78. socket.emit("private message", {
  79. fromUser: fromUserSocket,
  80. message: message.message,
  81. to: chatId,
  82. toUser:toUserSocket
  83. })
  84. if(toUserSocket){
  85. socket.emit('privat chat', {
  86. user: fromUserSocket,
  87. to: chatId,
  88. toUser: toUserSocket
  89. })
  90. }
  91. }
  92. useEffect(() => {
  93. if(socket) {
  94. socket.on('writing', (data) => {
  95. setUserTyping(data)
  96. setTimeout(() => setUserTyping([]), 500 )
  97. })
  98. }
  99. }, [socket, token])
  100. useEffect(() => {
  101. if(token && socket){
  102. socketEvents(socket)
  103. }
  104. }, [token, socket, user])
  105. return (
  106. <div className='rootContainer'>
  107. <Box className = {isTabletorMobile?'mobileRootBox':'rootBox'}>
  108. <Box className={isTabletorMobile?'usersBoxMobile':'usersBox'}
  109. sx = {showUserInfoBox ? {
  110. transform: "translateX(100%)",
  111. display: "block"
  112. }: { }}>
  113. <UserInfo/>
  114. { isTabletorMobile ? <SwitchButton/> : null}
  115. <Button
  116. style={isTabletorMobile ?
  117. {
  118. maxHeight:'20px',
  119. maxWidth: '15px',
  120. fontSize: '10px',
  121. marginLeft: '25px',
  122. marginRight: '10px'}
  123. :{margin:'10px 5px'}}
  124. variant="contained"
  125. onClick={()=> {
  126. localStorage.removeItem('token');
  127. dispatch(removeToken());
  128. socket.disconnect();
  129. }}>
  130. Logout
  131. </Button>
  132. </Box>
  133. <Box className ={isTabletorMobile ? 'rootMessageFormMobile':'rootMessageForm'} >
  134. {isCamActiv ?
  135. <div>
  136. <Button
  137. variant="contained"
  138. component="label"
  139. onClick = {() => webcamEventHandler()}
  140. >
  141. close camera
  142. </Button>
  143. <WebcamCapture />
  144. </div>
  145. :
  146. ""}
  147. {isPrivatChat? <PrivateChat/> : <MessageForm/>}
  148. {isUserTyping.isTyping && !isPrivatChat &&(isUserTyping.userName !== user.userName)? <span> User {isUserTyping.userName} typing..</span> : ""}
  149. <Box
  150. component="form"
  151. onSubmit = {e => {
  152. e.preventDefault()
  153. if (message.message.length){
  154. isPrivatChat? sendPrivateMessage() : dispatch(sendMessage({user, socket}));
  155. // isPrivatChat && dispatch(getSocket('allmessages'))
  156. isPrivatChat &&dispatch(editMessage({editMessage: ''}))
  157. setMessage({message: ''})
  158. play()
  159. }
  160. }}
  161. sx={(isTabletorMobile)?{
  162. display: 'flex',
  163. margin: '10px 2px'}
  164. :{
  165. display: 'flex',
  166. margin: '20px 5px'}
  167. }>
  168. <Button
  169. disabled={showSpinner}
  170. variant="contained"
  171. component="label"
  172. sx = {showSpinner?
  173. {
  174. minWidth: 'auto'}
  175. :
  176. {
  177. minWidth: 'auto',
  178. backgroundImage:'url(' + imgBtn + ')' ,
  179. backgroundPosition: 'center',
  180. backgroundRepeat: "no-repeat",
  181. backgroundSize: '20px 20px'
  182. }}
  183. style = {{color: 'green'}}
  184. >
  185. <input
  186. onChange={e =>{
  187. setshowSpinner(true)
  188. dispatch(fileMessage({files: e.target.files, axiosConfig}))
  189. }}
  190. type="file"
  191. multiple
  192. hidden
  193. />
  194. {showSpinner? loadingPercentage : ""}
  195. </Button>
  196. <Button
  197. variant="contained"
  198. component="label"
  199. sx = {{
  200. minWidth: 'auto',
  201. backgroundImage:'url(' + imgBtnPhoto + ')' ,
  202. backgroundPosition: 'center',
  203. backgroundRepeat: "no-repeat",
  204. backgroundSize: '20px 20px'
  205. }}
  206. onClick = {() => webcamEventHandler()}
  207. >
  208. </Button>
  209. <TextareaAutosize
  210. id="outlined-basic"
  211. label="Type a message..."
  212. variant="outlined"
  213. value={message.message}
  214. placeholder='type you message...'
  215. minRows={2}
  216. maxRows={4}
  217. className='textArea'
  218. onKeyPress={(e) => {
  219. if (e.key === "Enter") {
  220. e.preventDefault();
  221. isPrivatChat? sendPrivateMessage() : dispatch(sendMessage({user, socket}));
  222. //dispatch(getSocket('allmessages'))
  223. dispatch(editMessage({editMessage: ''}))
  224. setMessage({message: ''})
  225. }
  226. }}
  227. onChange={e => {
  228. dispatch(storeMessage({message: e.target.value}))
  229. !isPrivatChat &&socket.emit('userWriting');
  230. setMessage({message: e.target.value})}
  231. }
  232. // onFocus={ e => {
  233. // if (isTabletorMobile) {
  234. // e.target.className = 'focus'
  235. // }
  236. // }}
  237. // onBlur={e=> {
  238. // e.target.className = 'blur'
  239. // }}
  240. />
  241. <Button
  242. variant="contained"
  243. type='submit'
  244. disabled={user?.isMutted || !message.message.length}
  245. style={{width: '20%'}}
  246. >
  247. Send
  248. </Button>
  249. </Box>
  250. </Box>
  251. </Box>
  252. </div>
  253. )
  254. }