From f07993cf713825b5411f21c80ea5d837cc7f6304 Mon Sep 17 00:00:00 2001 From: KostiantynU <115901013+KostiantynU@users.noreply.github.com> Date: Tue, 9 Jul 2024 23:45:37 +0300 Subject: [PATCH] Changed settings of query for refetch onFocus, and by time - since websocket dose not work, we need to get an actual information from server about new messages, and topics. Added a util function for localLogOut. Added listener in store for onFocus for query. --- .../Topics/ChatsBlock/ChatItem/ChatItem.jsx | 2 +- src/common/Topics/ChatsBlock/ChatsBlock.jsx | 23 +++++--- src/components/Chat/Chat.jsx | 26 +++++---- .../TopicSettingsMenu/TopicSettingsMenu.jsx | 11 ++-- src/components/Layouts/Footer/Footer.jsx | 25 +++++++- .../Header/HeaderUserInfo/HeaderUserInfo.jsx | 6 +- .../LoginPageComponent/LoginPageComponent.jsx | 4 +- src/hooks/useWebSocketConnection.js | 25 +++++++- src/pages/TopicsPage/TopicsPage.jsx | 6 +- src/redux/chat-operations.js | 58 +++++++++++-------- src/redux/store.js | 4 ++ src/redux/topics-operations.js | 6 +- src/utils/localLogOutUtil.js | 17 ++++++ 13 files changed, 148 insertions(+), 65 deletions(-) create mode 100644 src/utils/localLogOutUtil.js diff --git a/src/common/Topics/ChatsBlock/ChatItem/ChatItem.jsx b/src/common/Topics/ChatsBlock/ChatItem/ChatItem.jsx index da600c2..3e3ea60 100644 --- a/src/common/Topics/ChatsBlock/ChatItem/ChatItem.jsx +++ b/src/common/Topics/ChatsBlock/ChatItem/ChatItem.jsx @@ -24,7 +24,7 @@ const ChatItem = ({ isActive, data }) => { const unreadedMessages = data?.unreadMessageCount ?? null; const lastMessageContent = data?.lastMessage ?? null; - const isTablet = useMediaQuery({ query: '(min-width: 769px' }); + const isTablet = useMediaQuery({ query: '(min-width: 768px' }); return ( { const { isTopics } = useTopicsContext(); @@ -31,10 +32,17 @@ const ChatsBlock = ({ filter }) => { const { pathname } = useLocation(); const path = pathname.includes('topics') ? 'topics' : 'notification'; const accessTokenInStore = useSelector(selectAccessToken); - const { data, isLoading, isError, error } = useGetAllQuery({ - filter, - accessTokenInStore, - }); + const { data, isLoading, isError, error } = useGetAllQuery( + { + filter, + accessTokenInStore, + }, + { + refetchOnMountOrArgChange: 5, + refetchOnFocus: true, + refetchOnReconnect: true, + }, + ); const { localLogOut } = useUser(); const navigate = useNavigate(); const dispatch = useDispatch(); @@ -43,9 +51,10 @@ const ChatsBlock = ({ filter }) => { if (error) { alert('Виникла помилка під час отримання тем (ChatsBlock)'); - dispatch(setIsLoggedIn(false)); - localStorage.removeItem('accessToken'); - localStorage.removeItem('refreshToken'); + localLogOutUtil(dispatch); + // dispatch(setIsLoggedIn(false)); + // localStorage.removeItem('accessToken'); + // localStorage.removeItem('refreshToken'); } return isLoading ? ( diff --git a/src/components/Chat/Chat.jsx b/src/components/Chat/Chat.jsx index d63da98..1cd7e86 100644 --- a/src/components/Chat/Chat.jsx +++ b/src/components/Chat/Chat.jsx @@ -25,8 +25,8 @@ import { subscribeToMessages, unsubscribeFromMessages, getTopicHistory, - sendMessage, connectWebSocket, + sendMessageByWs, } from '../../redux/chat-operations'; import { Avatars } from '../../ui-kit/images/avatars'; @@ -75,6 +75,7 @@ import { useSendMessageToTopicMutation, } from '../../redux/messagesAPI/messagesAPI'; import { selectAccessToken } from '../../redux/authOperatonsToolkit/authOperationsThunkSelectors'; +import localLogOutUtil from '../../utils/localLogOutUtil'; const Chat = ({ children }) => { const { title: topicId } = useParams(); @@ -89,8 +90,9 @@ const Chat = ({ children }) => { data: messagesByTopic, currentData: currentMessagesByTopic, isFetching, + error: messagesByTopicError, } = useGetMessagesByTopicQuery(topicId, { - refetchOnMountOrArgChange: true, + refetchOnMountOrArgChange: 5, refetchOnFocus: true, refetchOnReconnect: true, }); @@ -130,7 +132,7 @@ const Chat = ({ children }) => { dispatch(clearNotifications()); // dispatch(toggleChatOpened()); - dispatch(setChatOpened(true)); + dispatch(setChatOpened(false)); }; // eslint-disable-next-line react-hooks/exhaustive-deps @@ -180,19 +182,21 @@ const Chat = ({ children }) => { currentMessagesByTopic, ]); - if (isError || sendMessageError) { - if (sendMessageError.data?.message.includes('subscribed to the topic')) { + if (isError || sendMessageError || messagesByTopicError) { + if (sendMessageError?.data?.message?.includes('subscribed to the topic')) { return alert( 'Потрібно підписатись на цю тему, щоб відправляти повідомлення', ); } + alert('Виникла помилка під час отримання теми (ChatComponent)'); - dispatch(setIsLoggedIn(false)); - dispatch(setAccessToken(null)); - dispatch(setRefreshToken(null)); - localStorage.removeItem('accessToken'); - localStorage.removeItem('refreshToken'); + localLogOutUtil(dispatch); + // dispatch(setIsLoggedIn(false)); + // dispatch(setAccessToken(null)); + // dispatch(setRefreshToken(null)); + // localStorage.removeItem('accessToken'); + // localStorage.removeItem('refreshToken'); } const handleContacts = () => { @@ -206,7 +210,7 @@ const Chat = ({ children }) => { return alert('This message is empty'); if (connected) { - // dispatch(sendMessage(topicId, inputMessage)); + // dispatch(sendMessageByWs({ topicId, inputMessage })); sendMessageToTopic({ topicId, inputMessage, accessTokenInStore }); inputRef.current.value = ''; } else { diff --git a/src/components/Chat/TopicSettingsMenu/TopicSettingsMenu.jsx b/src/components/Chat/TopicSettingsMenu/TopicSettingsMenu.jsx index 80fee90..3db7778 100644 --- a/src/components/Chat/TopicSettingsMenu/TopicSettingsMenu.jsx +++ b/src/components/Chat/TopicSettingsMenu/TopicSettingsMenu.jsx @@ -28,6 +28,7 @@ import { setIsLoggedIn, setRefreshToken, } from '../../../redux/authOperatonsToolkit/authOperationsThunkSlice'; +import localLogOutUtil from '../../../utils/localLogOutUtil'; const TopicSettingsMenu = ({ topicId, subscribeStatus }) => { const [anchorEl, setAnchorEl] = useState(null); @@ -50,10 +51,10 @@ const TopicSettingsMenu = ({ topicId, subscribeStatus }) => { alert( 'Виникла помилка під час отримання улюблених тем, авторизуйтесь в системмі!', ); - - dispatch(setIsLoggedIn(false)); - dispatch(setAccessToken(null)); - return dispatch(setRefreshToken(null)); + localLogOutUtil(dispatch); + // dispatch(setIsLoggedIn(false)); + // dispatch(setAccessToken(null)); + // return dispatch(setRefreshToken(null)); } } @@ -69,7 +70,7 @@ const TopicSettingsMenu = ({ topicId, subscribeStatus }) => { const handleAddFavourite = async () => { try { - const { error } = await addFavourite(topicId); + const { error } = await addFavourite({ topicId, accessTokenInStore }); if (error) { alert('Виникла помилка під час додання теми до улюблених'); } else { diff --git a/src/components/Layouts/Footer/Footer.jsx b/src/components/Layouts/Footer/Footer.jsx index 5ee52d0..3c5cf16 100644 --- a/src/components/Layouts/Footer/Footer.jsx +++ b/src/components/Layouts/Footer/Footer.jsx @@ -6,7 +6,14 @@ import { FooterWrap, LogOutButton, LogOutIcon } from './Footer.styled'; // import { PATH } from '../../../constans/routes'; import { useDispatch, useSelector } from 'react-redux'; import { selectIsLoggedIn } from '../../../redux/authOperatonsToolkit/authOperationsThunkSelectors'; -import { setIsLoggedIn } from '../../../redux/authOperatonsToolkit/authOperationsThunkSlice'; +// import { +// setAccessToken, +// setIsLoggedIn, +// setRefreshToken, +// } from '../../../redux/authOperatonsToolkit/authOperationsThunkSlice'; +// import { setConnected } from '../../../redux/chatSlice'; +// import { client } from '../../../redux/chat-operations'; +import localLogOutUtil from '../../../utils/localLogOutUtil'; const Footer = () => { // const { isAuthenticated, localLogOut } = useUser(); @@ -22,10 +29,22 @@ const Footer = () => { if (error) { alert(error.data.message); - dispatch(setIsLoggedIn(false)); + // dispatch(setIsLoggedIn(false)); + // dispatch(setConnected(false)); + // dispatch(setAccessToken(null)); + // dispatch(setRefreshToken(null)); + // client.deactivate(); + localLogOutUtil(dispatch); + return; } - + localLogOutUtil(dispatch); + // dispatch(setIsLoggedIn(false)); + // dispatch(setAccessToken(null)); + // dispatch(setConnected(false)); + // dispatch(setAccessToken(null)); + // dispatch(setRefreshToken(null)); + // client.deactivate(); // localLogOut(); // navigate(PATH.MAIN); } catch (error) { diff --git a/src/components/Layouts/Header/HeaderUserInfo/HeaderUserInfo.jsx b/src/components/Layouts/Header/HeaderUserInfo/HeaderUserInfo.jsx index 0dca0e3..8700f41 100644 --- a/src/components/Layouts/Header/HeaderUserInfo/HeaderUserInfo.jsx +++ b/src/components/Layouts/Header/HeaderUserInfo/HeaderUserInfo.jsx @@ -21,9 +21,9 @@ import { UserInfoBlock, UserName, } from './HeaderUserInfo.styled'; -import { setIsLoggedIn } from '../../../../redux/authOperatonsToolkit/authOperationsThunkSlice'; // eslint-disable-next-line max-len import { selectAccessToken } from '../../../../redux/authOperatonsToolkit/authOperationsThunkSelectors'; +import localLogOutUtil from '../../../../utils/localLogOutUtil'; // import { selectUserThunk } from '../../../../redux/userApiThunk/userApiThunkSelectors'; // import { useUser } from '../../../../hooks/useUser'; // import { useNavigate } from 'react-router-dom'; @@ -53,9 +53,7 @@ const HeaderUserInfo = () => { alert( 'Сталася помилка при отриманні інформації про користувача - необхідно авторизуватись', ); - dispatch(setIsLoggedIn(false)); - localStorage.removeItem('accessToken'); - localStorage.removeItem('refreshToken'); + localLogOutUtil(dispatch); } // localLogOut(); // navigate(PATH.MAIN); diff --git a/src/components/LoginPageComponent/LoginPageComponent.jsx b/src/components/LoginPageComponent/LoginPageComponent.jsx index 5c1b814..be983d9 100644 --- a/src/components/LoginPageComponent/LoginPageComponent.jsx +++ b/src/components/LoginPageComponent/LoginPageComponent.jsx @@ -56,7 +56,9 @@ function LoginPageComponent() { } else if (error.data) { alert(error.data.message); } else { - alert('Something goes wrong :-( Maybe server is down.'); + alert( + 'Something goes wrong :-( Maybe server or your connection is down.', + ); } return; } diff --git a/src/hooks/useWebSocketConnection.js b/src/hooks/useWebSocketConnection.js index 9a80dcb..e1fc86e 100644 --- a/src/hooks/useWebSocketConnection.js +++ b/src/hooks/useWebSocketConnection.js @@ -2,24 +2,43 @@ import { useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { client } from '../redux/chat-operations'; import { selectConnected, setConnected } from '../redux/chatSlice'; +// import { connectWebSocket } from '../redux/chat-operations'; +import SockJS from 'sockjs-client'; +import { selectAccessToken } from '../redux/authOperatonsToolkit/authOperationsThunkSelectors'; +import { BASE_URL } from '../redux/apiParams'; +import localLogOutUtil from '../utils/localLogOutUtil'; export const useWebSocketConnection = (isLoggedIn) => { const dispatch = useDispatch(); const connected = useSelector(selectConnected); - const stompClient = client; + const accessTokenInStore = useSelector(selectAccessToken); useEffect(() => { if (isLoggedIn && !connected) { // dispatch(connectWebSocket()); + const socket = new SockJS( + `${BASE_URL}/chat?Authorization=Bearer ${accessTokenInStore}`, + ); - stompClient.activate(); - stompClient.configure({ + socket.onmessage = function () { + // I think it don't work. + }; + + client.configure({ onConnect: (frame) => { if (frame.command === 'CONNECTED') { dispatch(setConnected(true)); } }, + onWebSocketError: () => { + localLogOutUtil(dispatch); + }, + webSocketFactory: function () { + return socket; + }, }); + + client.activate(); } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/src/pages/TopicsPage/TopicsPage.jsx b/src/pages/TopicsPage/TopicsPage.jsx index c5045e8..6581544 100644 --- a/src/pages/TopicsPage/TopicsPage.jsx +++ b/src/pages/TopicsPage/TopicsPage.jsx @@ -10,10 +10,12 @@ import { useTopicsPageContext } from './TopicsPageContext'; import { useTopicsContext } from '../../common/Topics/TopicsContext'; const useTabletAndBelowMediaQuery = () => - useMediaQuery({ query: '(max-width: 770px)' }); + // useMediaQuery({ query: '(max-width: 770px)' }); + useMediaQuery({ query: '(max-width: 768px)' }); const useMobileMediaQuery = () => - useMediaQuery({ query: '(max-width: 769px)' }); + // useMediaQuery({ query: '(max-width: 769px)' }); + useMediaQuery({ query: '(max-width: 767px)' }); function TopicsPage() { const isTabletAndBelow = useTabletAndBelowMediaQuery(); diff --git a/src/redux/chat-operations.js b/src/redux/chat-operations.js index 38dd916..a9304dc 100644 --- a/src/redux/chat-operations.js +++ b/src/redux/chat-operations.js @@ -1,9 +1,10 @@ /* eslint-disable no-unused-vars */ -import SockJS from 'sockjs-client'; +// import SockJS from 'sockjs-client'; // eslint-disable-next-line import/no-unresolved import { Client } from '@stomp/stompjs'; +// import { Stomp } from '@stomp/stompjs'; -import { BASE_URL } from './apiParams'; +// import { BASE_URL } from './apiParams'; import { setAllTopicsNotifications, @@ -31,18 +32,22 @@ const sendToPublicTopicDest = '/app/topic/public/'; // const socket = new SockJS( // `${BASE_URL}/chat?Authorization=Bearer ${localStorage.getItem('accessToken')}` // ); +// socket.onerror = function (error) { +// console.log(error); +// }; const stompConfig = { heartbeatIncoming: 7000, heartbeatOutgoing: 7000, reconnectDelay: 10000, - webSocketFactory: function () { - return new SockJS( - `${BASE_URL}/chat?Authorization=Bearer ${localStorage.getItem( - 'accessToken', - )}`, - ); - }, + // webSocketFactory: function () { + // return new SockJS( + // `${BASE_URL}/chat?Authorization=Bearer ${localStorage.getItem( + // 'accessToken', + // )}`, + // ); + // return socket; + // }, }; export const client = new Client(stompConfig); @@ -64,6 +69,7 @@ export const connectWebSocket = () => { // const socket = new SockJS( // `${BASE_URL}/chat?Authorization=Bearer ${localStorage.getItem('accessToken')}` // ); + // client = Stomp.over(() => socket); // console.log('client', client); // await new Promise((resolve, reject) => { @@ -147,13 +153,19 @@ export const unsubscribeFromMessages = () => { export const disconnectWebSocket = () => { return async (dispatch) => { try { - await new Promise((resolve, _) => { - client.disconnect(() => { + // await new Promise((resolve, _) => { + // client.disconnect(() => { + // resolve(); + // }); + // }); + await new Promise((resolve, reject) => { + client.deactivate((event) => { resolve(); }); + if (!client.connected) { + dispatch(setConnected(false)); + } }); - - dispatch(setConnected(false)); } catch (error) { console.error('Error disconnecting from WebSocket:', error); } @@ -163,17 +175,13 @@ export const disconnectWebSocket = () => { export const subscribeToAllTopicsNotify = () => { return async (dispatch) => { try { - // const subscriptionToAllNotify = await client.subscribe( - // `${subToAllTopicsNotificationsDest}`, - // message => { - // const parsedAllTopicsNotifications = JSON.parse(message.body); - - // dispatch( - // setAllTopicsNotifications(message.id ? - // [{ name: `${message.id}` }] : [{ name: 'test' }]) - // ); - // } - // ); + // const subscriptionToAllNotify = await client.subscribe( + // `${subToAllTopicsNotificationsDest}`, + // (message) => { + // // console.log('message', message); + // const parsedAllTopicsNotifications = JSON.parse(message.body); + // // console.log('parsedAllTopicsNotifications', parsedAllTopicsNotifications); + // dispatch(setAllTopicsNotifications(parsedAllTopicsNotifications)); const subscriptionToAllNotify = await client.subscribe( subToAllTopicsNotificationsDest, (message) => { @@ -299,7 +307,7 @@ export const getTopicHistory = (topicId) => { }; }; -export const sendMessage = (topicId, inputMessage) => { +export const sendMessageByWs = ({ topicId, inputMessage }) => { return async () => { await client.publish({ destination: `${sendToPublicTopicDest}${topicId}`, diff --git a/src/redux/store.js b/src/redux/store.js index 9793c13..2493f54 100644 --- a/src/redux/store.js +++ b/src/redux/store.js @@ -1,4 +1,6 @@ import { configureStore } from '@reduxjs/toolkit'; +import { setupListeners } from '@reduxjs/toolkit/query'; + import authenticationApi from './auth-operations'; import topicsApi from './topics-operations'; import userApi from './user-operations'; @@ -26,3 +28,5 @@ const store = configureStore({ }); export default store; + +setupListeners(store.dispatch); diff --git a/src/redux/topics-operations.js b/src/redux/topics-operations.js index 5709a79..c7ce3eb 100644 --- a/src/redux/topics-operations.js +++ b/src/redux/topics-operations.js @@ -35,13 +35,13 @@ const topicsApi = createApi({ }), addFavourite: builder.mutation({ - query: (id) => ({ - url: `/topics/${id}/favourite/add`, + query: ({ topicId, accessTokenInStore }) => ({ + url: `/topics/${topicId}/favourite/add`, method: 'PATCH', headers: { Referer: Referer, 'Content-Type': 'application/json', - Authorization: `Bearer ${localStorage.getItem('accessToken')}`, + Authorization: `Bearer ${accessTokenInStore}`, }, }), invalidatesTags: ['Topics'], diff --git a/src/utils/localLogOutUtil.js b/src/utils/localLogOutUtil.js new file mode 100644 index 0000000..d63f5c1 --- /dev/null +++ b/src/utils/localLogOutUtil.js @@ -0,0 +1,17 @@ +import { + setAccessToken, + setIsLoggedIn, + setRefreshToken, +} from '../redux/authOperatonsToolkit/authOperationsThunkSlice'; +import { disconnectWebSocket } from '../redux/chat-operations'; + +const localLogOutUtil = (dispatch) => { + dispatch(setIsLoggedIn(false)); + dispatch(setAccessToken(null)); + dispatch(setRefreshToken(null)); + dispatch(disconnectWebSocket()); + localStorage.removeItem('accessToken'); + localStorage.removeItem('refreshToken'); +}; + +export default localLogOutUtil;