From 967a38cc0796a0a84a9da77858a9aa66aa347215 Mon Sep 17 00:00:00 2001 From: Ashaba Derrick Date: Tue, 15 Apr 2025 14:35:58 +0300 Subject: [PATCH 1/5] changes to BuddyMatcher --- client/src/components/BuddyMatcher.jsx | 28 +++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/client/src/components/BuddyMatcher.jsx b/client/src/components/BuddyMatcher.jsx index e5f84445..7b2bc814 100644 --- a/client/src/components/BuddyMatcher.jsx +++ b/client/src/components/BuddyMatcher.jsx @@ -3,10 +3,10 @@ import { NEW_EVENT_CHAT_RESTORE, NEW_EVENT_CLOSED, NEW_EVENT_INACTIVE, - NEW_EVENT_JOIN, NEW_EVENT_JOINED, } from '../../../constants.json'; import { connectWithId, socket } from 'src/lib/socketConnection'; +import useBuddyUtils from 'src/lib/buddysocket'; import { useCallback, useEffect, useRef, useState } from 'react'; import Anonymous from 'components/Anonymous'; @@ -28,6 +28,7 @@ const BuddyMatcher = () => { const { createChat, closeChat, closeAllChats } = useChat(); const { startSearch, endSearch, app } = useApp(); const { setLoadingText, startNewSearch } = useCloseChat(); + const { joinSearch, setupSocketListeners } = useBuddyUtils(socket); const [disconnected, setDisconnected] = useState(false); const reconnectAttempts = useRef(0); @@ -73,7 +74,7 @@ const BuddyMatcher = () => { const onConnect = useCallback(() => { // Here server will be informed that user is searching for // another user - socket.emit(NEW_EVENT_JOIN, { + joinSearch({ loginId: authState.loginId, email: authState.email, }); @@ -132,6 +133,16 @@ const BuddyMatcher = () => { }; setupSocket(); + const cleanupListeners = setupSocketListeners({ + onConnectHandler: onConnect, + onUserJoinedHandler: onUserJoined, + onRestoreChatHandler: onRestoreChat, + onCloseHandler: onClose, + onInactiveHandler: onInactive, + onDisconnectHandler: onDisconnect, + onReconnectAttemptHandler: onReconnectAttempt, + onReconnectErrorHandler: onReconnectError, + }); socket.on('connect', onConnect); socket.on(NEW_EVENT_CLOSED, onClose); @@ -143,18 +154,7 @@ const BuddyMatcher = () => { socket.io.on('reconnect_error', onReconnectError); return () => { - socket - .off('connect', onConnect) - .off(NEW_EVENT_JOINED, onUserJoined) - .off(NEW_EVENT_CHAT_RESTORE, onRestoreChat) - .off(NEW_EVENT_CLOSED, onClose) - .off(NEW_EVENT_INACTIVE, onInactive) - .off('disconnect', onDisconnect); - - socket.io - .off('reconnect_attempt', onReconnectAttempt) - .off('reconnect_error', onReconnectError); - + cleanupListeners(); socket.disconnect(); }; }, [app.currentChatId]); From 3d1c427302a1d2f8f261a7857ce1a9f025c6260b Mon Sep 17 00:00:00 2001 From: Ashaba Derrick Date: Tue, 15 Apr 2025 14:36:31 +0300 Subject: [PATCH 2/5] added buddySocket.js --- client/src/lib/buddySocket.js | 70 +++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 client/src/lib/buddySocket.js diff --git a/client/src/lib/buddySocket.js b/client/src/lib/buddySocket.js new file mode 100644 index 00000000..600c754b --- /dev/null +++ b/client/src/lib/buddySocket.js @@ -0,0 +1,70 @@ +/** + * @typedef {import('socket.io-client').Socket} Socket + */ +import { + NEW_EVENT_CHAT_RESTORE, + NEW_EVENT_CLOSED, + NEW_EVENT_INACTIVE, + NEW_EVENT_JOIN, + NEW_EVENT_JOINED, +} from '../../../constants.json'; + +/** + * + * @param {Socket} socket + */ +export default function useBuddyUtils(socket) { + function joinSearch({ loginId, email }) { + return new Promise((resolve, reject) => { + if (!socket.connected) { + reject(null); + return; + } + + socket.timeout(30000).emit(NEW_EVENT_JOIN, { loginId, email }, (err, response) => { + if (err) { + reject(err); + return; + } + + resolve(response); + }); + }); + } + + function setupSocketListeners({ + onConnectHandler, + onUserJoinedHandler, + onRestoreChatHandler, + onCloseHandler, + onInactiveHandler, + onDisconnectHandler, + onReconnectAttemptHandler, + onReconnectErrorHandler, + }) { + socket.on('connect', onConnectHandler); + socket.on(NEW_EVENT_JOINED, onUserJoinedHandler); + socket.on(NEW_EVENT_CHAT_RESTORE, onRestoreChatHandler); + socket.on(NEW_EVENT_CLOSED, onCloseHandler); + socket.on(NEW_EVENT_INACTIVE, onInactiveHandler); + socket.on('disconnect', onDisconnectHandler); + socket.io.on('reconnect_attempt', onReconnectAttemptHandler); + socket.io.on('reconnect_error', onReconnectErrorHandler); + + return () => { + socket.off('connect', onConnectHandler); + socket.off(NEW_EVENT_JOINED, onUserJoinedHandler); + socket.off(NEW_EVENT_CHAT_RESTORE, onRestoreChatHandler); + socket.off(NEW_EVENT_CLOSED, onCloseHandler); + socket.off(NEW_EVENT_INACTIVE, onInactiveHandler); + socket.off('disconnect', onDisconnectHandler); + socket.io.off('reconnect_attempt', onReconnectAttemptHandler); + socket.io.off('reconnect_error', onReconnectErrorHandler); + }; + } + + return { + joinSearch, + setupSocketListeners, + }; +} \ No newline at end of file From aab0ac36c3e733ddd69ce8b9773bb0bfc275bf94 Mon Sep 17 00:00:00 2001 From: Ashaba Derrick Date: Thu, 24 Apr 2025 12:00:10 +0300 Subject: [PATCH 3/5] changed the name of import buddySocket --- client/src/components/BuddyMatcher.jsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/src/components/BuddyMatcher.jsx b/client/src/components/BuddyMatcher.jsx index 7b2bc814..062c45cc 100644 --- a/client/src/components/BuddyMatcher.jsx +++ b/client/src/components/BuddyMatcher.jsx @@ -6,9 +6,8 @@ import { NEW_EVENT_JOINED, } from '../../../constants.json'; import { connectWithId, socket } from 'src/lib/socketConnection'; -import useBuddyUtils from 'src/lib/buddysocket'; +import useBuddyUtils from 'src/lib/buddySocket'; import { useCallback, useEffect, useRef, useState } from 'react'; - import Anonymous from 'components/Anonymous'; import { createBrowserNotification } from 'src/lib/browserNotification'; import { isExplicitDisconnection } from 'src/lib/utils'; From 01076ea9cb8686057f4f8908c2cf2c25c8f46f54 Mon Sep 17 00:00:00 2001 From: Ashaba Derrick Date: Mon, 5 May 2025 20:03:51 +0300 Subject: [PATCH 4/5] refactor: improve socket handling and code organization - Move socket-related functions from BuddyMatcher to buddySocket - Add reconnection attempt management in buddySocket - Extract long components in BuddyMatcher for better readability - Fix line length issues in both files --- client/src/components/BuddyMatcher.jsx | 64 ++++---------------------- client/src/lib/buddySocket.js | 55 +++++++++++++++++++--- 2 files changed, 57 insertions(+), 62 deletions(-) diff --git a/client/src/components/BuddyMatcher.jsx b/client/src/components/BuddyMatcher.jsx index 062c45cc..6a3941bc 100644 --- a/client/src/components/BuddyMatcher.jsx +++ b/client/src/components/BuddyMatcher.jsx @@ -1,13 +1,7 @@ import { useNavigate } from 'react-router-dom'; -import { - NEW_EVENT_CHAT_RESTORE, - NEW_EVENT_CLOSED, - NEW_EVENT_INACTIVE, - NEW_EVENT_JOINED, -} from '../../../constants.json'; import { connectWithId, socket } from 'src/lib/socketConnection'; import useBuddyUtils from 'src/lib/buddySocket'; -import { useCallback, useEffect, useRef, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import Anonymous from 'components/Anonymous'; import { createBrowserNotification } from 'src/lib/browserNotification'; import { isExplicitDisconnection } from 'src/lib/utils'; @@ -27,31 +21,9 @@ const BuddyMatcher = () => { const { createChat, closeChat, closeAllChats } = useChat(); const { startSearch, endSearch, app } = useApp(); const { setLoadingText, startNewSearch } = useCloseChat(); - const { joinSearch, setupSocketListeners } = useBuddyUtils(socket); + const { joinSearch, setupSocketListeners, disconnect, handleReconnect } = useBuddyUtils(socket); const [disconnected, setDisconnected] = useState(false); - const reconnectAttempts = useRef(0); - - function disconnect() { - reconnectAttempts.current = 0; - if (app.currentChatId) { - return; - } - - socket.disconnect(); - setDisconnected(true); - endSearch(); - } - - async function handleReconnect() { - if (socket.connected) { - return; - } - - startSearch(); - setLoadingText(defaultLoadingText); - await connectWithId(app.currentChatId); - } const onUserJoined = useCallback(({ roomId, userIds }) => { playNotification('buddyPaired'); @@ -71,8 +43,6 @@ const BuddyMatcher = () => { }, []); const onConnect = useCallback(() => { - // Here server will be informed that user is searching for - // another user joinSearch({ loginId: authState.loginId, email: authState.email, @@ -103,17 +73,7 @@ const BuddyMatcher = () => { return; } - disconnect(); - }, []); - - const onReconnectAttempt = useCallback((attempts) => { - reconnectAttempts.current = attempts; - }, []); - - const onReconnectError = useCallback(() => { - if (reconnectAttempts.current >= 3) { - disconnect(); - } + disconnect(app.currentChatId, () => setDisconnected(true), endSearch); }, []); useEffect(() => { @@ -139,19 +99,8 @@ const BuddyMatcher = () => { onCloseHandler: onClose, onInactiveHandler: onInactive, onDisconnectHandler: onDisconnect, - onReconnectAttemptHandler: onReconnectAttempt, - onReconnectErrorHandler: onReconnectError, }); - socket.on('connect', onConnect); - socket.on(NEW_EVENT_CLOSED, onClose); - socket.on(NEW_EVENT_JOINED, onUserJoined); - socket.on(NEW_EVENT_CHAT_RESTORE, onRestoreChat); - socket.on(NEW_EVENT_INACTIVE, onInactive); - socket.on('disconnect', onDisconnect); - socket.io.on('reconnect_attempt', onReconnectAttempt); - socket.io.on('reconnect_error', onReconnectError); - return () => { cleanupListeners(); socket.disconnect(); @@ -162,7 +111,12 @@ const BuddyMatcher = () => { navigate('/searching'); } - return disconnected ? : ; + const handleReconnectClick = useCallback(() => { + handleReconnect(app.currentChatId, startSearch, setLoadingText, defaultLoadingText); + }, [app.currentChatId, startSearch, setLoadingText]); + + const reconnectBanner = ; + return disconnected ? reconnectBanner : ; }; export default BuddyMatcher; diff --git a/client/src/lib/buddySocket.js b/client/src/lib/buddySocket.js index 600c754b..ede3e778 100644 --- a/client/src/lib/buddySocket.js +++ b/client/src/lib/buddySocket.js @@ -8,12 +8,15 @@ import { NEW_EVENT_JOIN, NEW_EVENT_JOINED, } from '../../../constants.json'; +import { connectWithId } from './socketConnection'; /** * * @param {Socket} socket */ export default function useBuddyUtils(socket) { + let reconnectAttempts = 0; + function joinSearch({ loginId, email }) { return new Promise((resolve, reject) => { if (!socket.connected) { @@ -32,6 +35,42 @@ export default function useBuddyUtils(socket) { }); } + function disconnect(currentChatId, onDisconnected, onEndSearch) { + if (currentChatId) { + return; + } + + reconnectAttempts = 0; + socket.disconnect(); + onDisconnected(); + onEndSearch(); + } + + async function handleReconnect( + currentChatId, + onStartSearch, + onSetLoadingText, + defaultLoadingText + ) { + if (socket.connected) { + return; + } + + onStartSearch(); + onSetLoadingText(defaultLoadingText); + await connectWithId(currentChatId); + } + + function handleReconnectAttempt(attempt) { + reconnectAttempts = attempt; + } + + function handleReconnectError(onDisconnect) { + if (reconnectAttempts >= 3) { + onDisconnect(); + } + } + function setupSocketListeners({ onConnectHandler, onUserJoinedHandler, @@ -39,17 +78,17 @@ export default function useBuddyUtils(socket) { onCloseHandler, onInactiveHandler, onDisconnectHandler, - onReconnectAttemptHandler, - onReconnectErrorHandler, }) { + const reconnectErrorHandler = () => handleReconnectError(onDisconnectHandler); + socket.on('connect', onConnectHandler); socket.on(NEW_EVENT_JOINED, onUserJoinedHandler); socket.on(NEW_EVENT_CHAT_RESTORE, onRestoreChatHandler); socket.on(NEW_EVENT_CLOSED, onCloseHandler); socket.on(NEW_EVENT_INACTIVE, onInactiveHandler); socket.on('disconnect', onDisconnectHandler); - socket.io.on('reconnect_attempt', onReconnectAttemptHandler); - socket.io.on('reconnect_error', onReconnectErrorHandler); + socket.io.on('reconnect_attempt', handleReconnectAttempt); + socket.io.on('reconnect_error', reconnectErrorHandler); return () => { socket.off('connect', onConnectHandler); @@ -58,13 +97,15 @@ export default function useBuddyUtils(socket) { socket.off(NEW_EVENT_CLOSED, onCloseHandler); socket.off(NEW_EVENT_INACTIVE, onInactiveHandler); socket.off('disconnect', onDisconnectHandler); - socket.io.off('reconnect_attempt', onReconnectAttemptHandler); - socket.io.off('reconnect_error', onReconnectErrorHandler); + socket.io.off('reconnect_attempt', handleReconnectAttempt); + socket.io.off('reconnect_error', reconnectErrorHandler); }; } return { joinSearch, setupSocketListeners, + disconnect, + handleReconnect, }; -} \ No newline at end of file +} From 6be922ba9178c00446e68a516bd0f668ddf5d398 Mon Sep 17 00:00:00 2001 From: Ashaba Derrick Date: Sat, 24 May 2025 22:09:28 +0300 Subject: [PATCH 5/5] refactor(socket): centralize socket logic in buddySocket.js - Move all socket-related functions from BuddyMatcher.jsx to buddySocket.js - Create useSocketHandlers hook to manage all socket event handlers - Move socket event handlers and their memoization to buddySocket.js - Move socket reconnection logic to buddySocket.js - Improve code organization and separation of concerns - Enhance maintainability and reusability of socket functionality The changes centralize all socket-related logic in buddySocket.js while keeping BuddyMatcher.jsx focused on UI composition and component lifecycle management. --- client/src/components/BuddyMatcher.jsx | 106 ++------ client/src/lib/buddySocket.js | 335 +++++++++++++++++-------- 2 files changed, 249 insertions(+), 192 deletions(-) diff --git a/client/src/components/BuddyMatcher.jsx b/client/src/components/BuddyMatcher.jsx index 6a3941bc..772c1d88 100644 --- a/client/src/components/BuddyMatcher.jsx +++ b/client/src/components/BuddyMatcher.jsx @@ -1,10 +1,9 @@ import { useNavigate } from 'react-router-dom'; -import { connectWithId, socket } from 'src/lib/socketConnection'; +import { socket } from 'src/lib/socketConnection'; import useBuddyUtils from 'src/lib/buddySocket'; -import { useCallback, useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import Anonymous from 'components/Anonymous'; import { createBrowserNotification } from 'src/lib/browserNotification'; -import { isExplicitDisconnection } from 'src/lib/utils'; import { useApp } from 'src/context/AppContext'; import { useAuth } from 'src/context/AuthContext'; import { useChat } from 'src/context/ChatContext'; @@ -21,84 +20,24 @@ const BuddyMatcher = () => { const { createChat, closeChat, closeAllChats } = useChat(); const { startSearch, endSearch, app } = useApp(); const { setLoadingText, startNewSearch } = useCloseChat(); - const { joinSearch, setupSocketListeners, disconnect, handleReconnect } = useBuddyUtils(socket); + const { setupSocketListeners, handleReconnectClick, setupSocket } = useBuddyUtils(socket); const [disconnected, setDisconnected] = useState(false); - const onUserJoined = useCallback(({ roomId, userIds }) => { - playNotification('buddyPaired'); - createBrowserNotification( - "Let's Chat :)", - "You've found a match, don't keep your Partner waiting ⌛" - ); - createChat(roomId, userIds); - endSearch(roomId); - }, []); - - const onRestoreChat = useCallback(({ chats, currentChatId }) => { - Object.values(chats).forEach((chat) => { - createChat(chat.id, chat.userIds, chat.messages, chat.createdAt); - }); - endSearch(currentChatId); - }, []); - - const onConnect = useCallback(() => { - joinSearch({ - loginId: authState.loginId, - email: authState.email, - }); - setDisconnected(false); - }, []); - - const onClose = useCallback((chatId) => { - endSearch(); - closeChat(chatId); - playNotification('chatClosed'); - - if (!confirm('This chat is closed! Would you like to search for a new buddy?')) { - navigate('/'); - return; - } - - createBrowserNotification('Chat Closed', 'Your buddy left the chat'); - startNewSearch(); - }, []); - - const onInactive = useCallback(() => { - closeAllChats(); - }, []); - - const onDisconnect = useCallback((reason) => { - if (isExplicitDisconnection(reason)) { - return; - } - - disconnect(app.currentChatId, () => setDisconnected(true), endSearch); - }, []); - useEffect(() => { - const setupSocket = async () => { - if (!app.currentChatId) { - startSearch(); - } - - if (!socket.connected) { - try { - await connectWithId(app.currentChatId); - } catch (error) { - console.error('Failed to connect:', error); - } - } - }; - - setupSocket(); + setupSocket(app.currentChatId, startSearch); const cleanupListeners = setupSocketListeners({ - onConnectHandler: onConnect, - onUserJoinedHandler: onUserJoined, - onRestoreChatHandler: onRestoreChat, - onCloseHandler: onClose, - onInactiveHandler: onInactive, - onDisconnectHandler: onDisconnect, + authState, + playNotification, + createChat, + endSearch, + closeChat, + navigate, + createBrowserNotification, + startNewSearch, + closeAllChats, + app, + setDisconnected, }); return () => { @@ -111,11 +50,16 @@ const BuddyMatcher = () => { navigate('/searching'); } - const handleReconnectClick = useCallback(() => { - handleReconnect(app.currentChatId, startSearch, setLoadingText, defaultLoadingText); - }, [app.currentChatId, startSearch, setLoadingText]); - - const reconnectBanner = ; + const reconnectBanner = ( + + ); return disconnected ? reconnectBanner : ; }; diff --git a/client/src/lib/buddySocket.js b/client/src/lib/buddySocket.js index ede3e778..f0a71d10 100644 --- a/client/src/lib/buddySocket.js +++ b/client/src/lib/buddySocket.js @@ -1,111 +1,224 @@ -/** - * @typedef {import('socket.io-client').Socket} Socket - */ -import { - NEW_EVENT_CHAT_RESTORE, - NEW_EVENT_CLOSED, - NEW_EVENT_INACTIVE, - NEW_EVENT_JOIN, - NEW_EVENT_JOINED, -} from '../../../constants.json'; -import { connectWithId } from './socketConnection'; - -/** - * - * @param {Socket} socket - */ -export default function useBuddyUtils(socket) { - let reconnectAttempts = 0; - - function joinSearch({ loginId, email }) { - return new Promise((resolve, reject) => { - if (!socket.connected) { - reject(null); - return; - } - - socket.timeout(30000).emit(NEW_EVENT_JOIN, { loginId, email }, (err, response) => { - if (err) { - reject(err); - return; - } - - resolve(response); - }); - }); - } - - function disconnect(currentChatId, onDisconnected, onEndSearch) { - if (currentChatId) { - return; - } - - reconnectAttempts = 0; - socket.disconnect(); - onDisconnected(); - onEndSearch(); - } - - async function handleReconnect( - currentChatId, - onStartSearch, - onSetLoadingText, - defaultLoadingText - ) { - if (socket.connected) { - return; - } - - onStartSearch(); - onSetLoadingText(defaultLoadingText); - await connectWithId(currentChatId); - } - - function handleReconnectAttempt(attempt) { - reconnectAttempts = attempt; - } - - function handleReconnectError(onDisconnect) { - if (reconnectAttempts >= 3) { - onDisconnect(); - } - } - - function setupSocketListeners({ - onConnectHandler, - onUserJoinedHandler, - onRestoreChatHandler, - onCloseHandler, - onInactiveHandler, - onDisconnectHandler, - }) { - const reconnectErrorHandler = () => handleReconnectError(onDisconnectHandler); - - socket.on('connect', onConnectHandler); - socket.on(NEW_EVENT_JOINED, onUserJoinedHandler); - socket.on(NEW_EVENT_CHAT_RESTORE, onRestoreChatHandler); - socket.on(NEW_EVENT_CLOSED, onCloseHandler); - socket.on(NEW_EVENT_INACTIVE, onInactiveHandler); - socket.on('disconnect', onDisconnectHandler); - socket.io.on('reconnect_attempt', handleReconnectAttempt); - socket.io.on('reconnect_error', reconnectErrorHandler); - - return () => { - socket.off('connect', onConnectHandler); - socket.off(NEW_EVENT_JOINED, onUserJoinedHandler); - socket.off(NEW_EVENT_CHAT_RESTORE, onRestoreChatHandler); - socket.off(NEW_EVENT_CLOSED, onCloseHandler); - socket.off(NEW_EVENT_INACTIVE, onInactiveHandler); - socket.off('disconnect', onDisconnectHandler); - socket.io.off('reconnect_attempt', handleReconnectAttempt); - socket.io.off('reconnect_error', reconnectErrorHandler); - }; - } - - return { - joinSearch, - setupSocketListeners, - disconnect, - handleReconnect, - }; -} +/** + * @typedef {import('socket.io-client').Socket} Socket + */ +import { + NEW_EVENT_CHAT_RESTORE, + NEW_EVENT_CLOSED, + NEW_EVENT_INACTIVE, + NEW_EVENT_JOIN, + NEW_EVENT_JOINED, +} from '../../../constants.json'; +import { connectWithId } from './socketConnection'; +import { createBrowserNotification } from './browserNotification'; +import { isExplicitDisconnection } from './utils'; + +/** + * + * @param {Socket} socket + */ +export default function useBuddyUtils(socket) { + let reconnectAttempts = 0; + + async function setupSocket(currentChatId, startSearch) { + if (!currentChatId) { + startSearch(); + } + + if (!socket.connected) { + try { + await connectWithId(currentChatId); + } catch (error) { + console.error('Failed to connect:', error); + } + } + } + + function joinSearch({ loginId, email }) { + return new Promise((resolve, reject) => { + if (!socket.connected) { + reject(null); + return; + } + + socket.timeout(30000).emit(NEW_EVENT_JOIN, { loginId, email }, (err, response) => { + if (err) { + reject(err); + return; + } + + resolve(response); + }); + }); + } + + function disconnect(currentChatId, onDisconnected, onEndSearch) { + if (currentChatId) { + return; + } + + reconnectAttempts = 0; + socket.disconnect(); + onDisconnected(); + onEndSearch(); + } + + async function handleReconnect( + currentChatId, + onStartSearch, + onSetLoadingText, + defaultLoadingText + ) { + if (socket.connected) { + return; + } + + onStartSearch(); + onSetLoadingText(defaultLoadingText); + await connectWithId(currentChatId); + } + + function handleReconnectClick(currentChatId, startSearch, setLoadingText, defaultLoadingText) { + return () => handleReconnect(currentChatId, startSearch, setLoadingText, defaultLoadingText); + } + + function handleReconnectAttempt(attempt) { + reconnectAttempts = attempt; + } + + function handleReconnectError(onDisconnect) { + if (reconnectAttempts >= 3) { + onDisconnect(); + } + } + + function onUserJoined({ roomId, userIds }, { playNotification, createChat, endSearch }) { + playNotification('buddyPaired'); + createBrowserNotification( + "Let's Chat :)", + "You've found a match, don't keep your Partner waiting ⌛" + ); + createChat(roomId, userIds); + endSearch(roomId); + } + + function onRestoreChat({ chats, currentChatId }, { createChat, endSearch }) { + Object.values(chats).forEach((chat) => { + createChat(chat.id, chat.userIds, chat.messages, chat.createdAt); + }); + endSearch(currentChatId); + } + + function onConnect({ loginId, email }, { joinSearch, setDisconnected }) { + joinSearch({ + loginId, + email, + }); + setDisconnected(false); + } + + function onClose( + chatId, + { endSearch, closeChat, playNotification, navigate, createBrowserNotification, startNewSearch } + ) { + endSearch(); + closeChat(chatId); + playNotification('chatClosed'); + + if (!confirm('This chat is closed! Would you like to search for a new buddy?')) { + navigate('/'); + return; + } + + createBrowserNotification('Chat Closed', 'Your buddy left the chat'); + startNewSearch(); + } + + function onInactive({ closeAllChats }) { + closeAllChats(); + } + + function onDisconnect(reason, { app, disconnect, setDisconnected, endSearch }) { + if (isExplicitDisconnection(reason)) { + return; + } + + disconnect(app.currentChatId, () => setDisconnected(true), endSearch); + } + + function setupSocketListeners({ + authState, + playNotification, + createChat, + endSearch, + closeChat, + navigate, + createBrowserNotification, + startNewSearch, + closeAllChats, + app, + setDisconnected, + }) { + const onConnectHandler = () => + onConnect( + { loginId: authState.loginId, email: authState.email }, + { joinSearch, setDisconnected } + ); + + const onUserJoinedHandler = (data) => + onUserJoined(data, { playNotification, createChat, endSearch }); + + const onRestoreChatHandler = (data) => onRestoreChat(data, { createChat, endSearch }); + + const onCloseHandler = (chatId) => + onClose(chatId, { + endSearch, + closeChat, + playNotification, + navigate, + createBrowserNotification, + startNewSearch, + }); + + const onInactiveHandler = () => onInactive({ closeAllChats }); + + const onDisconnectHandler = (reason) => + onDisconnect(reason, { app, disconnect, setDisconnected, endSearch }); + + const reconnectErrorHandler = () => handleReconnectError(onDisconnectHandler); + + socket.on('connect', onConnectHandler); + socket.on(NEW_EVENT_JOINED, onUserJoinedHandler); + socket.on(NEW_EVENT_CHAT_RESTORE, onRestoreChatHandler); + socket.on(NEW_EVENT_CLOSED, onCloseHandler); + socket.on(NEW_EVENT_INACTIVE, onInactiveHandler); + socket.on('disconnect', onDisconnectHandler); + socket.io.on('reconnect_attempt', handleReconnectAttempt); + socket.io.on('reconnect_error', reconnectErrorHandler); + + return () => { + socket.off('connect', onConnectHandler); + socket.off(NEW_EVENT_JOINED, onUserJoinedHandler); + socket.off(NEW_EVENT_CHAT_RESTORE, onRestoreChatHandler); + socket.off(NEW_EVENT_CLOSED, onCloseHandler); + socket.off(NEW_EVENT_INACTIVE, onInactiveHandler); + socket.off('disconnect', onDisconnectHandler); + socket.io.off('reconnect_attempt', handleReconnectAttempt); + socket.io.off('reconnect_error', reconnectErrorHandler); + }; + } + + return { + joinSearch, + setupSocketListeners, + disconnect, + handleReconnect, + handleReconnectClick, + onUserJoined, + onRestoreChat, + onConnect, + onClose, + onInactive, + onDisconnect, + setupSocket, + }; +}