Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

DirectChat.js 6.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. import React, { useEffect, useMemo } from "react";
  2. import PropTypes from "prop-types";
  3. import { DirectChatContainer } from "./DirectChat.styled";
  4. import DirectChatHeaderTitle from "./DirectChatHeaderTitle/DirectChatHeaderTitle";
  5. import DirectChatHeader from "./DirectChatHeader/DirectChatHeader";
  6. import { useDispatch, useSelector } from "react-redux";
  7. import { useLocation, useRouteMatch } from "react-router-dom";
  8. import {
  9. addNewMessage,
  10. fetchChats,
  11. fetchOneChat,
  12. setOneChat,
  13. } from "../../store/actions/chat/chatActions";
  14. import {
  15. selectLatestChats,
  16. selectSelectedChat,
  17. } from "../../store/selectors/chatSelectors";
  18. import DirectChatContent from "./DirectChatContent/DirectChatContent";
  19. import { selectOffer } from "../../store/selectors/offersSelectors";
  20. import { fetchOneOffer } from "../../store/actions/offers/offersActions";
  21. import SkeletonDirectChat from "./SkeletonDirectChat/SkeletonDirectChat";
  22. import { selectIsLoadingByActionType } from "../../store/selectors/loadingSelectors";
  23. import { CHAT_SCOPE } from "../../store/actions/chat/chatActionConstants";
  24. import { selectUserId } from "../../store/selectors/loginSelectors";
  25. import {
  26. acceptExchangeSocket,
  27. addMesageListener,
  28. removeMessageListener,
  29. } from "../../socket/socket";
  30. import { makeErrorToastMessage } from "../../store/utils/makeToastMessage";
  31. import { useTranslation } from "react-i18next";
  32. import {
  33. selectExchange,
  34. selectRequester,
  35. } from "../../store/selectors/exchangeSelector";
  36. import {
  37. acceptExchange,
  38. setRequester,
  39. } from "../../store/actions/exchange/exchangeActions";
  40. import { convertLocalDateToUTCDate } from "../../util/helpers/dateHelpers";
  41. import requesterStatus from "../../constants/requesterStatus";
  42. import exchangeStatus from "../../constants/exchangeStatus";
  43. const DirectChat = () => {
  44. const chat = useSelector(selectSelectedChat);
  45. const allChats = useSelector(selectLatestChats);
  46. const offer = useSelector(selectOffer);
  47. const routeMatch = useRouteMatch();
  48. const location = useLocation();
  49. const dispatch = useDispatch();
  50. const { t } = useTranslation();
  51. const exchange = useSelector(selectExchange);
  52. const requester = useSelector(selectRequester);
  53. const userId = useSelector(selectUserId);
  54. const isLoadingDirectChat = useSelector(
  55. selectIsLoadingByActionType(CHAT_SCOPE)
  56. );
  57. const offerObject = useMemo(() => {
  58. if (location?.state?.offerId) {
  59. return offer?.offer;
  60. }
  61. return chat?.offer?.offer;
  62. }, [chat, location.state, offer]);
  63. const chatObject = useMemo(() => {
  64. if (location?.state?.offerId) {
  65. return {};
  66. }
  67. return chat;
  68. }, [chat, location.state]);
  69. const amIBuyer = useMemo(
  70. () => exchange.buyer?.userId === userId,
  71. [exchange, userId]
  72. );
  73. const exchangeState = useMemo(() => {
  74. if (exchange?.buyer) {
  75. let haveIAccepted = amIBuyer
  76. ? exchange.buyer?.accepted
  77. : exchange.seller?.accepted;
  78. let haveInterlucatorAccepted = amIBuyer
  79. ? exchange.seller?.accepted
  80. : exchange.buyer?.accepted;
  81. let haveIReviewed = amIBuyer
  82. ? exchange.buyer.givenReview
  83. : exchange.seller.givenReview;
  84. if (haveIAccepted) {
  85. if (haveInterlucatorAccepted) {
  86. if (haveIReviewed) {
  87. return exchangeStatus.REVIEWED;
  88. } else {
  89. return exchangeStatus.ACCEPTED;
  90. }
  91. } else {
  92. return exchangeStatus.I_OFFERED;
  93. }
  94. } else {
  95. if (haveInterlucatorAccepted) {
  96. return exchangeStatus.I_AM_OFFERED;
  97. } else {
  98. return exchangeStatus.INITIAL;
  99. }
  100. }
  101. }
  102. return exchangeStatus.INITIAL;
  103. }, [exchange, amIBuyer]);
  104. const interlocutorObject = useMemo(() => {
  105. if (location?.state?.offerId) {
  106. return {
  107. image: offer?.companyData?.image,
  108. name: offer?.companyData?.company?.name,
  109. location: offer?.companyData?.company?.contacts?.location,
  110. userId: offer?.offer?.userId,
  111. telephone: offer?.companyData?.company?.contacts?.telephone,
  112. };
  113. }
  114. return {
  115. ...chat?.interlocutor,
  116. userId:
  117. chat?.chat?.participants[0] === userId
  118. ? chat?.chat?.participants[1]
  119. : chat?.chat?.participants[0],
  120. };
  121. }, [chat, location.state, offer]);
  122. // Fetch chat after it is created
  123. // (renders after state of location changes)
  124. useEffect(() => {
  125. if (routeMatch.params.idChat) {
  126. refreshChat();
  127. }
  128. }, [routeMatch.params.idChat, location.state?.offerId]);
  129. // Listener to socket.IO chat
  130. useEffect(() => {
  131. addMesageListener(({ succeed, data }) => {
  132. if (succeed) {
  133. if (
  134. [...allChats].find((item) => {
  135. return item.chat._id === data.chatId;
  136. })
  137. ) {
  138. dispatch(
  139. addNewMessage({
  140. _id: data.chatId,
  141. message: data.message,
  142. })
  143. );
  144. if (
  145. data.message?.isAcceptRequest &&
  146. requester === requesterStatus.NOONE
  147. ) {
  148. dispatch(setRequester(requesterStatus.INTERLUCATOR));
  149. }
  150. } else {
  151. dispatch(fetchChats());
  152. }
  153. } else {
  154. dispatch(fetchChats());
  155. dispatch(fetchOneChat(routeMatch.params.idChat));
  156. makeErrorToastMessage(t("apiErrors.somethingWentWrong"));
  157. }
  158. });
  159. return () => removeMessageListener();
  160. }, [allChats, routeMatch]);
  161. const refreshChat = () => {
  162. if (routeMatch.params.idChat === "newMessage") {
  163. dispatch(fetchOneOffer(location.state.offerId));
  164. dispatch(setOneChat({}));
  165. } else {
  166. dispatch(fetchOneChat(routeMatch.params.idChat));
  167. }
  168. };
  169. const handleAcceptExchange = () => {
  170. acceptExchangeSocket(
  171. chat?.chat?._id,
  172. userId,
  173. chat?.interlocutor?._id,
  174. () => {
  175. dispatch(
  176. acceptExchange({
  177. exchangeId: exchange._id,
  178. })
  179. );
  180. dispatch(
  181. addNewMessage({
  182. _id: chat?.chat?._id,
  183. message: {
  184. userId,
  185. isAcceptRequest: true,
  186. text: "",
  187. _created: convertLocalDateToUTCDate(new Date()),
  188. },
  189. })
  190. );
  191. if (requester === requesterStatus.NOONE) {
  192. dispatch(setRequester(requesterStatus.ME));
  193. }
  194. }
  195. );
  196. };
  197. return (
  198. <DirectChatContainer>
  199. {isLoadingDirectChat || isLoadingDirectChat === undefined ? (
  200. <SkeletonDirectChat />
  201. ) : (
  202. <>
  203. <DirectChatHeaderTitle />
  204. <DirectChatHeader
  205. exchangeState={exchangeState}
  206. offer={offerObject}
  207. interlocutor={interlocutorObject}
  208. acceptExchange={handleAcceptExchange}
  209. />
  210. </>
  211. )}
  212. <DirectChatContent
  213. chat={chatObject}
  214. exchangeState={exchangeState}
  215. interlucator={interlocutorObject}
  216. refreshChat={refreshChat}
  217. />
  218. </DirectChatContainer>
  219. );
  220. };
  221. DirectChat.propTypes = {
  222. children: PropTypes.node,
  223. };
  224. export default DirectChat;