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:
@@ -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 = () => {
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|||||||
@@ -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',
|
||||||
|
};
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user