refactor(frontend): Phase 2 - extract business logic into custom hooks
Separate concerns - move Socket.IO and form logic from components to reusable hooks New Hooks: - useForm: Generic form state management with handleChange/handleSubmit/reset - useEventChat: Extract Socket.IO logic from EventChatPage (156 lines) * Manages messages, active users, connection state * Handles send message, load older messages with scroll preservation * Real-time updates via Socket.IO event listeners - useMatchChat: Extract Socket.IO logic from MatchChatPage (115 lines) * Manages 1:1 chat messages and connection * Loads message history from API * Real-time message sync via Socket.IO Pages Refactored: - EventChatPage: 661 → 564 lines (-97 lines, -15%) - MatchChatPage: 517 → 446 lines (-71 lines, -14%) Benefits: - Cleaner component code - UI separated from business logic - Reusable hooks can be used in other components - Easier to test - hooks can be unit tested independently - Better code organization - single responsibility principle - 168 lines eliminated from pages, moved to 271 lines of reusable hooks Phase 2 Total: -168 lines Grand Total (Phase 1+2): -389 lines (-12%)
This commit is contained in:
180
frontend/src/hooks/useEventChat.js
Normal file
180
frontend/src/hooks/useEventChat.js
Normal file
@@ -0,0 +1,180 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { connectSocket, getSocket } from '../services/socket';
|
||||
import { eventsAPI } from '../services/api';
|
||||
|
||||
/**
|
||||
* Custom hook for Event Chat functionality
|
||||
* Extracts Socket.IO logic and chat state management from EventChatPage
|
||||
*
|
||||
* @param {string} slug - Event slug
|
||||
* @param {number} userId - Current user ID
|
||||
* @param {object} event - Event object (needed to check if event exists)
|
||||
* @param {object} messagesContainerRef - Ref to messages container for scroll management
|
||||
* @returns {object} Chat state and handlers
|
||||
*
|
||||
* @example
|
||||
* const {
|
||||
* messages,
|
||||
* isConnected,
|
||||
* activeUsers,
|
||||
* sendMessage,
|
||||
* newMessage,
|
||||
* setNewMessage,
|
||||
* loadOlderMessages,
|
||||
* loadingOlder,
|
||||
* hasMore
|
||||
* } = useEventChat(slug, user.id, event, messagesContainerRef);
|
||||
*/
|
||||
const useEventChat = (slug, userId, event, messagesContainerRef) => {
|
||||
// Chat state
|
||||
const [messages, setMessages] = useState([]);
|
||||
const [newMessage, setNewMessage] = useState('');
|
||||
const [activeUsers, setActiveUsers] = useState([]);
|
||||
const [isConnected, setIsConnected] = useState(false);
|
||||
const [loadingOlder, setLoadingOlder] = useState(false);
|
||||
const [hasMore, setHasMore] = useState(true);
|
||||
|
||||
// Socket.IO connection and event listeners
|
||||
useEffect(() => {
|
||||
if (!event) return;
|
||||
|
||||
// Connect to Socket.IO
|
||||
const socket = connectSocket();
|
||||
|
||||
if (!socket) {
|
||||
console.error('Failed to connect to socket');
|
||||
return;
|
||||
}
|
||||
|
||||
// Socket event listeners
|
||||
socket.on('connect', () => {
|
||||
setIsConnected(true);
|
||||
// Join event room
|
||||
socket.emit('join_event_room', { slug });
|
||||
});
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
setIsConnected(false);
|
||||
});
|
||||
|
||||
// Receive message history (initial 20 messages)
|
||||
socket.on('message_history', (history) => {
|
||||
setMessages(history);
|
||||
setHasMore(history.length === 20);
|
||||
});
|
||||
|
||||
// Receive new messages
|
||||
socket.on('event_message', (message) => {
|
||||
setMessages((prev) => [...prev, message]);
|
||||
});
|
||||
|
||||
// Receive active users list
|
||||
socket.on('active_users', (users) => {
|
||||
// Filter out duplicates and current user
|
||||
const uniqueUsers = users
|
||||
.filter((u, index, self) =>
|
||||
index === self.findIndex((t) => t.userId === u.userId)
|
||||
)
|
||||
.filter((u) => u.userId !== userId);
|
||||
setActiveUsers(uniqueUsers);
|
||||
});
|
||||
|
||||
// User joined notification
|
||||
socket.on('user_joined', (userData) => {
|
||||
console.log(`${userData.username} joined the room`);
|
||||
});
|
||||
|
||||
// User left notification
|
||||
socket.on('user_left', (userData) => {
|
||||
console.log(`${userData.username} left the room`);
|
||||
});
|
||||
|
||||
// Cleanup
|
||||
return () => {
|
||||
socket.emit('leave_event_room');
|
||||
socket.off('connect');
|
||||
socket.off('disconnect');
|
||||
socket.off('message_history');
|
||||
socket.off('event_message');
|
||||
socket.off('active_users');
|
||||
socket.off('user_joined');
|
||||
socket.off('user_left');
|
||||
};
|
||||
}, [event, slug, userId]);
|
||||
|
||||
/**
|
||||
* Send a message to the event chat
|
||||
*/
|
||||
const sendMessage = (e) => {
|
||||
e.preventDefault();
|
||||
if (!newMessage.trim()) return;
|
||||
|
||||
const socket = getSocket();
|
||||
if (!socket || !socket.connected) {
|
||||
alert('Not connected to chat server');
|
||||
return;
|
||||
}
|
||||
|
||||
// Send message via Socket.IO
|
||||
socket.emit('send_event_message', {
|
||||
content: newMessage,
|
||||
});
|
||||
|
||||
setNewMessage('');
|
||||
};
|
||||
|
||||
/**
|
||||
* Load older messages (pagination)
|
||||
* Preserves scroll position when loading older messages
|
||||
*/
|
||||
const loadOlderMessages = async () => {
|
||||
if (loadingOlder || !hasMore || messages.length === 0) return;
|
||||
|
||||
setLoadingOlder(true);
|
||||
try {
|
||||
const oldestMessageId = messages[0].id;
|
||||
const response = await eventsAPI.getMessages(slug, oldestMessageId, 20);
|
||||
|
||||
if (response.data.length > 0) {
|
||||
// Save current scroll position
|
||||
const container = messagesContainerRef.current;
|
||||
const oldScrollHeight = container?.scrollHeight || 0;
|
||||
const oldScrollTop = container?.scrollTop || 0;
|
||||
|
||||
// Prepend older messages
|
||||
setMessages((prev) => [...response.data, ...prev]);
|
||||
setHasMore(response.hasMore);
|
||||
|
||||
// Restore scroll position (adjust for new content)
|
||||
setTimeout(() => {
|
||||
if (container) {
|
||||
const newScrollHeight = container.scrollHeight;
|
||||
container.scrollTop = oldScrollTop + (newScrollHeight - oldScrollHeight);
|
||||
}
|
||||
}, 0);
|
||||
} else {
|
||||
setHasMore(false);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load older messages:', error);
|
||||
} finally {
|
||||
setLoadingOlder(false);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
// State
|
||||
messages,
|
||||
newMessage,
|
||||
setNewMessage,
|
||||
activeUsers,
|
||||
isConnected,
|
||||
loadingOlder,
|
||||
hasMore,
|
||||
// Actions
|
||||
sendMessage,
|
||||
loadOlderMessages
|
||||
};
|
||||
};
|
||||
|
||||
export default useEventChat;
|
||||
89
frontend/src/hooks/useForm.js
Normal file
89
frontend/src/hooks/useForm.js
Normal file
@@ -0,0 +1,89 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
/**
|
||||
* Custom hook for form state management
|
||||
*
|
||||
* Eliminates repetitive useState and onChange handlers for each form field
|
||||
*
|
||||
* @param {Object} initialValues - Initial form values { fieldName: value, ... }
|
||||
* @param {Function} onSubmit - Optional submit handler
|
||||
* @returns {Object} Form state and handlers
|
||||
*
|
||||
* @example
|
||||
* const { values, handleChange, handleSubmit, reset } = useForm(
|
||||
* { email: '', password: '' },
|
||||
* async (values) => { await login(values.email, values.password); }
|
||||
* );
|
||||
*
|
||||
* <input name="email" value={values.email} onChange={handleChange} />
|
||||
* <form onSubmit={handleSubmit}>...</form>
|
||||
*/
|
||||
const useForm = (initialValues = {}, onSubmit) => {
|
||||
const [values, setValues] = useState(initialValues);
|
||||
|
||||
/**
|
||||
* Generic change handler for all form fields
|
||||
* Works with any input that has name and value attributes
|
||||
*/
|
||||
const handleChange = (e) => {
|
||||
const { name, value, type, checked } = e.target;
|
||||
|
||||
// Handle checkboxes differently
|
||||
const fieldValue = type === 'checkbox' ? checked : value;
|
||||
|
||||
setValues(prev => ({
|
||||
...prev,
|
||||
[name]: fieldValue
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle form submission
|
||||
* Prevents default, calls optional onSubmit callback
|
||||
*/
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
if (onSubmit) {
|
||||
await onSubmit(values);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Reset form to initial values
|
||||
*/
|
||||
const reset = () => {
|
||||
setValues(initialValues);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set multiple values at once
|
||||
* Useful for pre-filling form from API data
|
||||
*/
|
||||
const setFormValues = (newValues) => {
|
||||
setValues(prev => ({
|
||||
...prev,
|
||||
...newValues
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
* Set a single field value programmatically
|
||||
*/
|
||||
const setValue = (name, value) => {
|
||||
setValues(prev => ({
|
||||
...prev,
|
||||
[name]: value
|
||||
}));
|
||||
};
|
||||
|
||||
return {
|
||||
values,
|
||||
handleChange,
|
||||
handleSubmit,
|
||||
reset,
|
||||
setFormValues,
|
||||
setValue
|
||||
};
|
||||
};
|
||||
|
||||
export default useForm;
|
||||
122
frontend/src/hooks/useMatchChat.js
Normal file
122
frontend/src/hooks/useMatchChat.js
Normal file
@@ -0,0 +1,122 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { connectSocket, getSocket } from '../services/socket';
|
||||
import { matchesAPI } from '../services/api';
|
||||
|
||||
/**
|
||||
* Custom hook for Match Chat functionality (1:1 private chat)
|
||||
* Extracts Socket.IO logic and chat state management from MatchChatPage
|
||||
*
|
||||
* @param {object} match - Match object with id and partner info
|
||||
* @param {number} userId - Current user ID
|
||||
* @param {string} slug - Match slug for API calls
|
||||
* @returns {object} Chat state and handlers
|
||||
*
|
||||
* @example
|
||||
* const {
|
||||
* messages,
|
||||
* isConnected,
|
||||
* sendMessage,
|
||||
* newMessage,
|
||||
* setNewMessage
|
||||
* } = useMatchChat(match, user.id, slug);
|
||||
*/
|
||||
const useMatchChat = (match, userId, slug) => {
|
||||
// Chat state
|
||||
const [messages, setMessages] = useState([]);
|
||||
const [newMessage, setNewMessage] = useState('');
|
||||
const [isConnected, setIsConnected] = useState(false);
|
||||
|
||||
// Load message history from API
|
||||
useEffect(() => {
|
||||
const loadMessages = async () => {
|
||||
if (!match || !slug) return;
|
||||
|
||||
try {
|
||||
const result = await matchesAPI.getMatchMessages(slug);
|
||||
setMessages(result.data || []);
|
||||
} catch (error) {
|
||||
console.error('Failed to load messages:', error);
|
||||
}
|
||||
};
|
||||
loadMessages();
|
||||
}, [match, slug]);
|
||||
|
||||
// Socket.IO connection and event listeners
|
||||
useEffect(() => {
|
||||
// Wait for match to be loaded
|
||||
if (!match) return;
|
||||
|
||||
// Connect to Socket.IO
|
||||
const socket = connectSocket();
|
||||
|
||||
if (!socket) {
|
||||
console.error('Failed to connect to socket');
|
||||
return;
|
||||
}
|
||||
|
||||
// Helper to join match room
|
||||
const joinMatchRoom = () => {
|
||||
setIsConnected(true);
|
||||
socket.emit('join_match_room', { matchId: match.id });
|
||||
console.log(`Joined match room ${match.id}`);
|
||||
};
|
||||
|
||||
// Socket event listeners
|
||||
socket.on('connect', joinMatchRoom);
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
setIsConnected(false);
|
||||
});
|
||||
|
||||
// Receive messages
|
||||
socket.on('match_message', (message) => {
|
||||
setMessages((prev) => [...prev, message]);
|
||||
});
|
||||
|
||||
// Join immediately if already connected
|
||||
if (socket.connected) {
|
||||
joinMatchRoom();
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
return () => {
|
||||
socket.off('connect', joinMatchRoom);
|
||||
socket.off('disconnect');
|
||||
socket.off('match_message');
|
||||
};
|
||||
}, [match, userId]);
|
||||
|
||||
/**
|
||||
* Send a message to the match chat
|
||||
*/
|
||||
const sendMessage = (e) => {
|
||||
e.preventDefault();
|
||||
if (!newMessage.trim() || !match) return;
|
||||
|
||||
const socket = getSocket();
|
||||
if (!socket || !socket.connected) {
|
||||
alert('Not connected to chat server');
|
||||
return;
|
||||
}
|
||||
|
||||
// Send message via Socket.IO using numeric match ID
|
||||
socket.emit('send_match_message', {
|
||||
matchId: match.id,
|
||||
content: newMessage,
|
||||
});
|
||||
|
||||
setNewMessage('');
|
||||
};
|
||||
|
||||
return {
|
||||
// State
|
||||
messages,
|
||||
newMessage,
|
||||
setNewMessage,
|
||||
isConnected,
|
||||
// Actions
|
||||
sendMessage
|
||||
};
|
||||
};
|
||||
|
||||
export default useMatchChat;
|
||||
@@ -11,6 +11,7 @@ import ChatMessageList from '../components/chat/ChatMessageList';
|
||||
import ChatInput from '../components/chat/ChatInput';
|
||||
import ConfirmationModal from '../components/modals/ConfirmationModal';
|
||||
import Modal from '../components/modals/Modal';
|
||||
import useEventChat from '../hooks/useEventChat';
|
||||
|
||||
const EventChatPage = () => {
|
||||
const { slug } = useParams();
|
||||
@@ -19,18 +20,25 @@ const EventChatPage = () => {
|
||||
const [event, setEvent] = useState(null);
|
||||
const [isParticipant, setIsParticipant] = useState(false);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [messages, setMessages] = useState([]);
|
||||
const [newMessage, setNewMessage] = useState('');
|
||||
const [activeUsers, setActiveUsers] = useState([]);
|
||||
const [checkedInUsers, setCheckedInUsers] = useState([]);
|
||||
const [isConnected, setIsConnected] = useState(false);
|
||||
const [loadingOlder, setLoadingOlder] = useState(false);
|
||||
const [hasMore, setHasMore] = useState(true);
|
||||
const [showLeaveModal, setShowLeaveModal] = useState(false);
|
||||
const [isLeaving, setIsLeaving] = useState(false);
|
||||
const messagesEndRef = useRef(null);
|
||||
const messagesContainerRef = useRef(null);
|
||||
|
||||
// Event Chat hook - manages messages, Socket.IO, and active users
|
||||
const {
|
||||
messages,
|
||||
newMessage,
|
||||
setNewMessage,
|
||||
activeUsers,
|
||||
isConnected,
|
||||
loadingOlder,
|
||||
hasMore,
|
||||
sendMessage: handleSendMessage,
|
||||
loadOlderMessages
|
||||
} = useEventChat(slug, user?.id, event, messagesContainerRef);
|
||||
|
||||
// Heats state
|
||||
const [myHeats, setMyHeats] = useState([]);
|
||||
const [userHeats, setUserHeats] = useState(new Map());
|
||||
@@ -116,62 +124,15 @@ const EventChatPage = () => {
|
||||
scrollToBottom();
|
||||
}, [messages]);
|
||||
|
||||
// Heats updates listener (specific to EventChatPage)
|
||||
useEffect(() => {
|
||||
if (!event) return;
|
||||
|
||||
// Connect to Socket.IO
|
||||
const socket = connectSocket();
|
||||
|
||||
if (!socket) {
|
||||
console.error('Failed to connect to socket');
|
||||
return;
|
||||
}
|
||||
|
||||
// Socket event listeners
|
||||
socket.on('connect', () => {
|
||||
setIsConnected(true);
|
||||
// Join event room
|
||||
socket.emit('join_event_room', { slug });
|
||||
});
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
setIsConnected(false);
|
||||
});
|
||||
|
||||
// Receive message history (initial 20 messages)
|
||||
socket.on('message_history', (history) => {
|
||||
setMessages(history);
|
||||
setHasMore(history.length === 20);
|
||||
});
|
||||
|
||||
// Receive new messages
|
||||
socket.on('event_message', (message) => {
|
||||
setMessages((prev) => [...prev, message]);
|
||||
});
|
||||
|
||||
// Receive active users list
|
||||
socket.on('active_users', (users) => {
|
||||
// Filter out duplicates and current user
|
||||
const uniqueUsers = users
|
||||
.filter((u, index, self) =>
|
||||
index === self.findIndex((t) => t.userId === u.userId)
|
||||
)
|
||||
.filter((u) => u.userId !== user.id);
|
||||
setActiveUsers(uniqueUsers);
|
||||
});
|
||||
|
||||
// User joined notification
|
||||
socket.on('user_joined', (userData) => {
|
||||
console.log(`${userData.username} joined the room`);
|
||||
});
|
||||
|
||||
// User left notification
|
||||
socket.on('user_left', (userData) => {
|
||||
console.log(`${userData.username} left the room`);
|
||||
});
|
||||
const socket = getSocket();
|
||||
if (!socket) return;
|
||||
|
||||
// Heats updated notification
|
||||
socket.on('heats_updated', (data) => {
|
||||
const handleHeatsUpdated = (data) => {
|
||||
const { userId, heats } = data;
|
||||
|
||||
// Update userHeats map
|
||||
@@ -190,72 +151,14 @@ const EventChatPage = () => {
|
||||
setMyHeats(heats || []);
|
||||
setShowHeatsBanner(heats.length === 0);
|
||||
}
|
||||
});
|
||||
|
||||
// Cleanup
|
||||
return () => {
|
||||
socket.emit('leave_event_room');
|
||||
socket.off('connect');
|
||||
socket.off('disconnect');
|
||||
socket.off('message_history');
|
||||
socket.off('event_message');
|
||||
socket.off('active_users');
|
||||
socket.off('user_joined');
|
||||
socket.off('user_left');
|
||||
socket.off('heats_updated');
|
||||
};
|
||||
}, [event, slug, user.id]);
|
||||
|
||||
const handleSendMessage = (e) => {
|
||||
e.preventDefault();
|
||||
if (!newMessage.trim()) return;
|
||||
socket.on('heats_updated', handleHeatsUpdated);
|
||||
|
||||
const socket = getSocket();
|
||||
if (!socket || !socket.connected) {
|
||||
alert('Not connected to chat server');
|
||||
return;
|
||||
}
|
||||
|
||||
// Send message via Socket.IO
|
||||
socket.emit('send_event_message', {
|
||||
content: newMessage,
|
||||
});
|
||||
|
||||
setNewMessage('');
|
||||
};
|
||||
|
||||
const loadOlderMessages = async () => {
|
||||
if (loadingOlder || !hasMore || messages.length === 0) return;
|
||||
|
||||
setLoadingOlder(true);
|
||||
try {
|
||||
const oldestMessageId = messages[0].id;
|
||||
const response = await eventsAPI.getMessages(slug, oldestMessageId, 20);
|
||||
|
||||
if (response.data.length > 0) {
|
||||
// Save current scroll position
|
||||
const container = messagesContainerRef.current;
|
||||
const oldScrollHeight = container.scrollHeight;
|
||||
const oldScrollTop = container.scrollTop;
|
||||
|
||||
// Prepend older messages
|
||||
setMessages((prev) => [...response.data, ...prev]);
|
||||
setHasMore(response.hasMore);
|
||||
|
||||
// Restore scroll position (adjust for new content)
|
||||
setTimeout(() => {
|
||||
const newScrollHeight = container.scrollHeight;
|
||||
container.scrollTop = oldScrollTop + (newScrollHeight - oldScrollHeight);
|
||||
}, 0);
|
||||
} else {
|
||||
setHasMore(false);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load older messages:', error);
|
||||
} finally {
|
||||
setLoadingOlder(false);
|
||||
}
|
||||
};
|
||||
return () => {
|
||||
socket.off('heats_updated', handleHeatsUpdated);
|
||||
};
|
||||
}, [event, user.id]);
|
||||
|
||||
const handleMatchWith = async (userId) => {
|
||||
try {
|
||||
|
||||
@@ -11,6 +11,7 @@ import WebRTCWarning from '../components/WebRTCWarning';
|
||||
import Avatar from '../components/common/Avatar';
|
||||
import ChatMessageList from '../components/chat/ChatMessageList';
|
||||
import ChatInput from '../components/chat/ChatInput';
|
||||
import useMatchChat from '../hooks/useMatchChat';
|
||||
|
||||
const MatchChatPage = () => {
|
||||
const { slug } = useParams();
|
||||
@@ -18,16 +19,22 @@ const MatchChatPage = () => {
|
||||
const navigate = useNavigate();
|
||||
const [match, setMatch] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [messages, setMessages] = useState([]);
|
||||
const [newMessage, setNewMessage] = useState('');
|
||||
const [selectedFile, setSelectedFile] = useState(null);
|
||||
const [showLinkInput, setShowLinkInput] = useState(false);
|
||||
const [videoLink, setVideoLink] = useState('');
|
||||
const [isConnected, setIsConnected] = useState(false);
|
||||
const [webrtcDetection, setWebrtcDetection] = useState(null);
|
||||
const messagesEndRef = useRef(null);
|
||||
const fileInputRef = useRef(null);
|
||||
|
||||
// Match Chat hook - manages messages and Socket.IO
|
||||
const {
|
||||
messages,
|
||||
newMessage,
|
||||
setNewMessage,
|
||||
isConnected,
|
||||
sendMessage: handleSendMessage
|
||||
} = useMatchChat(match, user?.id, slug);
|
||||
|
||||
// WebRTC hook
|
||||
const {
|
||||
connectionState,
|
||||
@@ -72,21 +79,6 @@ const MatchChatPage = () => {
|
||||
loadMatch();
|
||||
}, [slug, navigate]);
|
||||
|
||||
// Load message history
|
||||
useEffect(() => {
|
||||
const loadMessages = async () => {
|
||||
if (!match) return;
|
||||
|
||||
try {
|
||||
const result = await matchesAPI.getMatchMessages(slug);
|
||||
setMessages(result.data || []);
|
||||
} catch (error) {
|
||||
console.error('Failed to load messages:', error);
|
||||
}
|
||||
};
|
||||
loadMessages();
|
||||
}, [match, slug]);
|
||||
|
||||
const partner = match?.partner;
|
||||
|
||||
const scrollToBottom = () => {
|
||||
@@ -97,69 +89,6 @@ const MatchChatPage = () => {
|
||||
scrollToBottom();
|
||||
}, [messages]);
|
||||
|
||||
useEffect(() => {
|
||||
// Wait for match to be loaded
|
||||
if (!match) return;
|
||||
|
||||
// Connect to Socket.IO
|
||||
const socket = connectSocket();
|
||||
|
||||
if (!socket) {
|
||||
console.error('Failed to connect to socket');
|
||||
return;
|
||||
}
|
||||
|
||||
// Helper to join match room
|
||||
const joinMatchRoom = () => {
|
||||
setIsConnected(true);
|
||||
socket.emit('join_match_room', { matchId: match.id });
|
||||
console.log(`Joined match room ${match.id}`);
|
||||
};
|
||||
|
||||
// Socket event listeners
|
||||
socket.on('connect', joinMatchRoom);
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
setIsConnected(false);
|
||||
});
|
||||
|
||||
// Receive messages
|
||||
socket.on('match_message', (message) => {
|
||||
setMessages((prev) => [...prev, message]);
|
||||
});
|
||||
|
||||
// Join immediately if already connected
|
||||
if (socket.connected) {
|
||||
joinMatchRoom();
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
return () => {
|
||||
socket.off('connect', joinMatchRoom);
|
||||
socket.off('disconnect');
|
||||
socket.off('match_message');
|
||||
};
|
||||
}, [match, user.id]);
|
||||
|
||||
const handleSendMessage = (e) => {
|
||||
e.preventDefault();
|
||||
if (!newMessage.trim() || !match) return;
|
||||
|
||||
const socket = getSocket();
|
||||
if (!socket || !socket.connected) {
|
||||
alert('Not connected to chat server');
|
||||
return;
|
||||
}
|
||||
|
||||
// Send message via Socket.IO using numeric match ID
|
||||
socket.emit('send_match_message', {
|
||||
matchId: match.id,
|
||||
content: newMessage,
|
||||
});
|
||||
|
||||
setNewMessage('');
|
||||
};
|
||||
|
||||
const handleFileSelect = (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file && file.type.startsWith('video/')) {
|
||||
|
||||
Reference in New Issue
Block a user