refactor(frontend): add CONNECTION_STATE and SUGGESTION_TYPE constants

- Add CONNECTION_STATE (disconnected, connecting, connected, failed)
- Add SUGGESTION_TYPE (toBeRecorded, toRecord)
- Update useWebRTC.js to use CONNECTION_STATE
- Update MatchChatPage.jsx to use CONNECTION_STATE
- Update RecordingTab.jsx to use SUGGESTION_TYPE
This commit is contained in:
Radosław Gierwiało
2025-11-23 22:28:54 +01:00
parent b3a6d39d7a
commit 408317b974
5 changed files with 54 additions and 28 deletions

View File

@@ -2,7 +2,7 @@ import { useState, useEffect } from 'react';
import { Video, VideoOff, Clock, CheckCircle, XCircle, AlertTriangle, RefreshCw } from 'lucide-react'; import { Video, VideoOff, Clock, CheckCircle, XCircle, AlertTriangle, RefreshCw } from 'lucide-react';
import { matchingAPI } from '../../services/api'; import { matchingAPI } from '../../services/api';
import Avatar from '../common/Avatar'; import Avatar from '../common/Avatar';
import { SUGGESTION_STATUS } from '../../constants'; import { SUGGESTION_STATUS, SUGGESTION_TYPE } from '../../constants';
/** /**
* RecordingTab - Main component for managing recording partnerships * RecordingTab - Main component for managing recording partnerships
@@ -199,7 +199,7 @@ const RecordingTab = ({ slug, event, myHeats }) => {
<SuggestionCard <SuggestionCard
key={suggestion.id || suggestion.heat?.id} key={suggestion.id || suggestion.heat?.id}
suggestion={suggestion} suggestion={suggestion}
type="toBeRecorded" type={SUGGESTION_TYPE.TO_BE_RECORDED}
onAccept={() => handleUpdateStatus(suggestion.id, SUGGESTION_STATUS.ACCEPTED)} onAccept={() => handleUpdateStatus(suggestion.id, SUGGESTION_STATUS.ACCEPTED)}
onReject={() => handleUpdateStatus(suggestion.id, SUGGESTION_STATUS.REJECTED)} onReject={() => handleUpdateStatus(suggestion.id, SUGGESTION_STATUS.REJECTED)}
/> />
@@ -225,7 +225,7 @@ const RecordingTab = ({ slug, event, myHeats }) => {
<SuggestionCard <SuggestionCard
key={suggestion.id || suggestion.heat?.id} key={suggestion.id || suggestion.heat?.id}
suggestion={suggestion} suggestion={suggestion}
type="toRecord" type={SUGGESTION_TYPE.TO_RECORD}
onAccept={() => handleUpdateStatus(suggestion.id, SUGGESTION_STATUS.ACCEPTED)} onAccept={() => handleUpdateStatus(suggestion.id, SUGGESTION_STATUS.ACCEPTED)}
onReject={() => handleUpdateStatus(suggestion.id, SUGGESTION_STATUS.REJECTED)} onReject={() => handleUpdateStatus(suggestion.id, SUGGESTION_STATUS.REJECTED)}
/> />
@@ -273,8 +273,8 @@ const SuggestionCard = ({ suggestion, type, onAccept, onReject }) => {
? `${heat.division?.abbreviation || '?'} ${heat.competitionType?.abbreviation || '?'} H${heat.heatNumber}` ? `${heat.division?.abbreviation || '?'} ${heat.competitionType?.abbreviation || '?'} H${heat.heatNumber}`
: 'Unknown heat'; : 'Unknown heat';
const person = type === 'toBeRecorded' ? recorder : dancer; const person = type === SUGGESTION_TYPE.TO_BE_RECORDED ? recorder : dancer;
const personLabel = type === 'toBeRecorded' ? 'Nagrywa Cie:' : 'Nagrywasz:'; const personLabel = type === SUGGESTION_TYPE.TO_BE_RECORDED ? 'Nagrywa Cie:' : 'Nagrywasz:';
// Status badge // Status badge
const getStatusBadge = () => { const getStatusBadge = () => {

View File

@@ -1 +1,7 @@
export { MATCH_STATUS, SUGGESTION_STATUS, MATCH_FILTER } from './statuses'; export {
MATCH_STATUS,
SUGGESTION_STATUS,
MATCH_FILTER,
CONNECTION_STATE,
SUGGESTION_TYPE,
} from './statuses';

View File

@@ -26,3 +26,21 @@ export const MATCH_FILTER = {
PENDING: 'pending', PENDING: 'pending',
ACCEPTED: 'accepted', ACCEPTED: 'accepted',
}; };
/**
* WebRTC connection states
*/
export const CONNECTION_STATE = {
DISCONNECTED: 'disconnected',
CONNECTING: 'connecting',
CONNECTED: 'connected',
FAILED: 'failed',
};
/**
* Recording suggestion types
*/
export const SUGGESTION_TYPE = {
TO_BE_RECORDED: 'toBeRecorded',
TO_RECORD: 'toRecord',
};

View File

@@ -1,5 +1,6 @@
import { useState, useEffect, useRef, useCallback } from 'react'; import { useState, useEffect, useRef, useCallback } from 'react';
import { getSocket } from '../services/socket'; import { getSocket } from '../services/socket';
import { CONNECTION_STATE } from '../constants';
// WebRTC configuration with STUN and TURN servers for NAT traversal // WebRTC configuration with STUN and TURN servers for NAT traversal
const rtcConfig = { const rtcConfig = {
@@ -39,7 +40,7 @@ const CHUNK_SIZE = 16384;
* @returns {Object} WebRTC state and control functions * @returns {Object} WebRTC state and control functions
*/ */
export const useWebRTC = (matchId, userId) => { export const useWebRTC = (matchId, userId) => {
const [connectionState, setConnectionState] = useState('disconnected'); // disconnected, connecting, connected, failed const [connectionState, setConnectionState] = useState(CONNECTION_STATE.DISCONNECTED);
const [transferProgress, setTransferProgress] = useState(0); const [transferProgress, setTransferProgress] = useState(0);
const [isTransferring, setIsTransferring] = useState(false); const [isTransferring, setIsTransferring] = useState(false);
const [receivingFile, setReceivingFile] = useState(null); const [receivingFile, setReceivingFile] = useState(null);
@@ -117,7 +118,7 @@ export const useWebRTC = (matchId, userId) => {
console.log('🔄 Connection state:', pc.connectionState); console.log('🔄 Connection state:', pc.connectionState);
setConnectionState(pc.connectionState); setConnectionState(pc.connectionState);
if (pc.connectionState === 'failed') { if (pc.connectionState === CONNECTION_STATE.FAILED) {
console.error('❌ WebRTC connection failed'); console.error('❌ WebRTC connection failed');
cleanupConnection(); cleanupConnection();
} }
@@ -154,17 +155,17 @@ export const useWebRTC = (matchId, userId) => {
const setupDataChannelHandlers = useCallback((dc) => { const setupDataChannelHandlers = useCallback((dc) => {
dc.onopen = () => { dc.onopen = () => {
console.log('✅ DataChannel opened'); console.log('✅ DataChannel opened');
setConnectionState('connected'); setConnectionState(CONNECTION_STATE.CONNECTED);
}; };
dc.onclose = () => { dc.onclose = () => {
console.log('❌ DataChannel closed'); console.log('❌ DataChannel closed');
setConnectionState('disconnected'); setConnectionState(CONNECTION_STATE.DISCONNECTED);
}; };
dc.onerror = (error) => { dc.onerror = (error) => {
console.error('❌ DataChannel error:', error); console.error('❌ DataChannel error:', error);
setConnectionState('failed'); setConnectionState(CONNECTION_STATE.FAILED);
}; };
dc.onmessage = (event) => { dc.onmessage = (event) => {
@@ -239,7 +240,7 @@ export const useWebRTC = (matchId, userId) => {
*/ */
const createOffer = useCallback(async () => { const createOffer = useCallback(async () => {
try { try {
setConnectionState('connecting'); setConnectionState(CONNECTION_STATE.CONNECTING);
const pc = initializePeerConnection(); const pc = initializePeerConnection();
createDataChannel(pc); createDataChannel(pc);
@@ -257,7 +258,7 @@ export const useWebRTC = (matchId, userId) => {
console.log('📤 Sent WebRTC offer'); console.log('📤 Sent WebRTC offer');
} catch (error) { } catch (error) {
console.error('Failed to create offer:', error); console.error('Failed to create offer:', error);
setConnectionState('failed'); setConnectionState(CONNECTION_STATE.FAILED);
} }
}, [initializePeerConnection, createDataChannel]); }, [initializePeerConnection, createDataChannel]);
@@ -266,7 +267,7 @@ export const useWebRTC = (matchId, userId) => {
*/ */
const handleOffer = useCallback(async (offer) => { const handleOffer = useCallback(async (offer) => {
try { try {
setConnectionState('connecting'); setConnectionState(CONNECTION_STATE.CONNECTING);
const pc = initializePeerConnection(); const pc = initializePeerConnection();
@@ -293,7 +294,7 @@ export const useWebRTC = (matchId, userId) => {
console.log('📤 Sent WebRTC answer'); console.log('📤 Sent WebRTC answer');
} catch (error) { } catch (error) {
console.error('Failed to handle offer:', error); console.error('Failed to handle offer:', error);
setConnectionState('failed'); setConnectionState(CONNECTION_STATE.FAILED);
} }
}, [initializePeerConnection, setupDataChannelHandlers]); }, [initializePeerConnection, setupDataChannelHandlers]);
@@ -312,7 +313,7 @@ export const useWebRTC = (matchId, userId) => {
console.log('✅ Remote description set (answer). ICE should connect now...'); console.log('✅ Remote description set (answer). ICE should connect now...');
} catch (error) { } catch (error) {
console.error('Failed to handle answer:', error); console.error('Failed to handle answer:', error);
setConnectionState('failed'); setConnectionState(CONNECTION_STATE.FAILED);
} }
}, []); }, []);
@@ -414,7 +415,7 @@ export const useWebRTC = (matchId, userId) => {
peerConnectionRef.current = null; peerConnectionRef.current = null;
} }
setConnectionState('disconnected'); setConnectionState(CONNECTION_STATE.DISCONNECTED);
setIsTransferring(false); setIsTransferring(false);
setTransferProgress(0); setTransferProgress(0);
setReceivingFile(null); setReceivingFile(null);

View File

@@ -14,6 +14,7 @@ import ChatInput from '../components/chat/ChatInput';
import useMatchChat from '../hooks/useMatchChat'; import useMatchChat from '../hooks/useMatchChat';
import FileTransferProgress from '../components/webrtc/FileTransferProgress'; import FileTransferProgress from '../components/webrtc/FileTransferProgress';
import LinkShareInput from '../components/webrtc/LinkShareInput'; import LinkShareInput from '../components/webrtc/LinkShareInput';
import { CONNECTION_STATE } from '../constants';
const MatchChatPage = () => { const MatchChatPage = () => {
const { slug } = useParams(); const { slug } = useParams();
@@ -104,7 +105,7 @@ const MatchChatPage = () => {
if (!selectedFile) return; if (!selectedFile) return;
// If not connected, initiate connection first // If not connected, initiate connection first
if (connectionState !== 'connected') { if (connectionState !== CONNECTION_STATE.CONNECTED) {
console.log('Creating WebRTC offer...'); console.log('Creating WebRTC offer...');
await createOffer(); await createOffer();
@@ -112,11 +113,11 @@ const MatchChatPage = () => {
const waitForConnection = new Promise((resolve, reject) => { const waitForConnection = new Promise((resolve, reject) => {
const timeout = setTimeout(() => reject(new Error('Connection timeout')), 30000); const timeout = setTimeout(() => reject(new Error('Connection timeout')), 30000);
const checkConnection = setInterval(() => { const checkConnection = setInterval(() => {
if (connectionState === 'connected') { if (connectionState === CONNECTION_STATE.CONNECTED) {
clearInterval(checkConnection); clearInterval(checkConnection);
clearTimeout(timeout); clearTimeout(timeout);
resolve(); resolve();
} else if (connectionState === 'failed') { } else if (connectionState === CONNECTION_STATE.FAILED) {
clearInterval(checkConnection); clearInterval(checkConnection);
clearTimeout(timeout); clearTimeout(timeout);
reject(new Error('Connection failed')); reject(new Error('Connection failed'));
@@ -177,11 +178,11 @@ const MatchChatPage = () => {
const getWebRTCStatusColor = () => { const getWebRTCStatusColor = () => {
switch (connectionState) { switch (connectionState) {
case 'connected': case CONNECTION_STATE.CONNECTED:
return 'text-green-600'; return 'text-green-600';
case 'connecting': case CONNECTION_STATE.CONNECTING:
return 'text-yellow-600'; return 'text-yellow-600';
case 'failed': case CONNECTION_STATE.FAILED:
return 'text-red-600'; return 'text-red-600';
default: default:
return 'text-gray-400'; return 'text-gray-400';
@@ -190,11 +191,11 @@ const MatchChatPage = () => {
const getWebRTCStatusText = () => { const getWebRTCStatusText = () => {
switch (connectionState) { switch (connectionState) {
case 'connected': case CONNECTION_STATE.CONNECTED:
return 'Connected (P2P)'; return 'Connected (P2P)';
case 'connecting': case CONNECTION_STATE.CONNECTING:
return 'Connecting...'; return 'Connecting...';
case 'failed': case CONNECTION_STATE.FAILED:
return 'Connection failed'; return 'Connection failed';
default: default:
return 'Ready to connect'; return 'Ready to connect';
@@ -258,7 +259,7 @@ const MatchChatPage = () => {
{/* WebRTC Status Bar */} {/* WebRTC Status Bar */}
<div className="bg-gray-50 border-b px-4 py-2 flex items-center justify-between"> <div className="bg-gray-50 border-b px-4 py-2 flex items-center justify-between">
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<div className={`w-2 h-2 rounded-full ${connectionState === 'connected' ? 'bg-green-500' : 'bg-gray-300'}`} /> <div className={`w-2 h-2 rounded-full ${connectionState === CONNECTION_STATE.CONNECTED ? 'bg-green-500' : 'bg-gray-300'}`} />
<span className={`text-sm font-medium ${getWebRTCStatusColor()}`}> <span className={`text-sm font-medium ${getWebRTCStatusColor()}`}>
{getWebRTCStatusText()} {getWebRTCStatusText()}
</span> </span>
@@ -270,7 +271,7 @@ const MatchChatPage = () => {
</span> </span>
)} )}
<span className="text-xs text-gray-500"> <span className="text-xs text-gray-500">
{connectionState === 'connected' ? '🔒 E2E Encrypted (DTLS)' : 'WebRTC P2P Ready'} {connectionState === CONNECTION_STATE.CONNECTED ? 'E2E Encrypted (DTLS)' : 'WebRTC P2P Ready'}
</span> </span>
</div> </div>
</div> </div>