1
1
'use client' ;
2
2
3
- import React , { useRef , useState , useEffect } from 'react' ;
3
+ import React , { useRef , useState } from 'react' ;
4
4
import io , { Socket } from 'socket.io-client' ;
5
5
import SimplePeer , { Instance } from 'simple-peer' ;
6
6
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' ;
@@ -15,59 +15,38 @@ const AudioSharing = () => {
15
15
const [ connectionStatus , setConnectionStatus ] = useState <
16
16
'Not Connected' | 'Connecting' | 'Connected'
17
17
> ( 'Not Connected' ) ;
18
-
19
- // Use refs for persistent state across remounts
20
18
const socketRef = useRef < Socket | null > ( null ) ;
21
19
const peerRef = useRef < Instance | null > ( null ) ;
22
20
const audioStreamRef = useRef < MediaStream | null > ( null ) ;
23
- const audioElementsRef = useRef < HTMLAudioElement [ ] > ( [ ] ) ;
24
- const mountCountRef = useRef ( 0 ) ;
25
- const lastCleanupTimeRef = useRef ( 0 ) ;
21
+ const initializedRef = useRef ( false ) ;
26
22
27
- // Constants
28
- const CLEANUP_THRESHOLD = 1000 ; // Minimum time between cleanups in ms
29
23
const SERVER_URL =
30
24
process . env . NEXT_PUBLIC_AUDIO_SERVER_URL || 'http://localhost:5555' ;
25
+
26
+ // Add TURN server credentials from environment variables
31
27
const TURN_SERVER = process . env . NEXT_PUBLIC_TURN_SERVER || '' ;
32
28
const TURN_USERNAME = process . env . NEXT_PUBLIC_TURN_USERNAME ;
33
29
const TURN_CREDENTIAL = process . env . NEXT_PUBLIC_TURN_PASSWORD ;
34
30
35
- const cleanupAudio = ( force = false ) => {
36
- const now = Date . now ( ) ;
37
-
38
- // Prevent rapid cleanup unless forced
39
- if ( ! force && now - lastCleanupTimeRef . current < CLEANUP_THRESHOLD ) {
40
- console . log ( 'Skipping cleanup due to threshold' ) ;
41
- return ;
42
- }
43
-
44
- console . log ( 'Cleaning up audio connections...' ) ;
45
- lastCleanupTimeRef . current = now ;
46
-
31
+ if ( ! TURN_SERVER || ! TURN_USERNAME || ! TURN_CREDENTIAL ) {
32
+ // Log which specific TURN variables are missing
33
+ console . error ( 'Missing TURN env:' , {
34
+ server : ! ! TURN_SERVER ,
35
+ username : ! ! TURN_USERNAME ,
36
+ credential : ! ! TURN_CREDENTIAL ,
37
+ } ) ;
38
+ }
39
+ const cleanupAudio = ( ) => {
47
40
if ( audioStreamRef . current ) {
48
41
audioStreamRef . current . getTracks ( ) . forEach ( ( track ) => {
49
42
track . stop ( ) ;
50
43
} ) ;
51
44
audioStreamRef . current = null ;
52
45
}
53
-
54
46
if ( peerRef . current ) {
55
47
peerRef . current . destroy ( ) ;
56
48
peerRef . current = null ;
57
49
}
58
-
59
- // Only disconnect socket if we're actually cleaning up (not remounting)
60
- if ( force && socketRef . current ) {
61
- socketRef . current . disconnect ( ) ;
62
- socketRef . current = null ;
63
- }
64
-
65
- audioElementsRef . current . forEach ( ( audio ) => {
66
- audio . pause ( ) ;
67
- audio . srcObject = null ;
68
- } ) ;
69
- audioElementsRef . current = [ ] ;
70
-
71
50
setIsAudioEnabled ( false ) ;
72
51
setConnectionStatus ( 'Not Connected' ) ;
73
52
} ;
@@ -82,8 +61,10 @@ const AudioSharing = () => {
82
61
trickle : false ,
83
62
config : {
84
63
iceServers : [
64
+ // Maintain existing STUN servers
85
65
{ urls : 'stun:stun.l.google.com:19302' } ,
86
66
{ urls : 'stun:global.stun.twilio.com:3478' } ,
67
+ // Add TURN server configuration
87
68
{
88
69
urls : TURN_SERVER ,
89
70
username : TURN_USERNAME ,
@@ -94,45 +75,42 @@ const AudioSharing = () => {
94
75
} ) ;
95
76
96
77
peer . on ( 'signal' , ( data : SignalData ) => {
78
+ console . log ( 'Sending signal data:' , data ) ;
97
79
socketRef . current ?. emit ( 'signal' , data ) ;
98
80
} ) ;
99
81
100
82
peer . on ( 'stream' , ( remoteStream : MediaStream ) => {
101
83
console . log ( 'Received remote stream' ) ;
102
84
const audio = new Audio ( ) ;
103
85
audio . srcObject = remoteStream ;
104
- audioElementsRef . current . push ( audio ) ;
105
86
audio
106
87
. play ( )
107
88
. catch ( ( error ) => console . error ( 'Error playing audio:' , error ) ) ;
108
89
} ) ;
109
90
110
91
peer . on ( 'error' , ( err : Error ) => {
111
92
console . error ( 'Peer connection error:' , err ) ;
112
- cleanupAudio ( true ) ;
93
+ cleanupAudio ( ) ;
113
94
} ) ;
114
95
115
96
peer . on ( 'close' , ( ) => {
116
97
console . log ( 'Peer connection closed' ) ;
117
- cleanupAudio ( true ) ;
98
+ cleanupAudio ( ) ;
118
99
} ) ;
119
100
101
+ // Add connection state logging
120
102
peer . on ( 'connect' , ( ) => {
121
- console . log ( 'Peer connection established' ) ;
103
+ console . log ( 'Peer connection established successfully ' ) ;
122
104
setConnectionStatus ( 'Connected' ) ;
123
105
} ) ;
124
106
125
107
return peer ;
126
108
} ;
127
109
128
110
const initializeSocketAndPeer = ( ) => {
129
- // If socket exists and is connected, reuse it
130
- if ( socketRef . current ?. connected ) {
131
- console . log ( 'Reusing existing socket connection' ) ;
132
- return ;
133
- }
111
+ if ( initializedRef . current ) return ;
112
+ initializedRef . current = true ;
134
113
135
- console . log ( 'Initializing new socket connection' ) ;
136
114
socketRef . current = io ( SERVER_URL , {
137
115
transports : [ 'websocket' ] ,
138
116
path : '/socket.io/' ,
@@ -147,10 +125,12 @@ const AudioSharing = () => {
147
125
148
126
socketRef . current . on ( 'connect_error' , ( error : Error ) => {
149
127
console . error ( 'Connection error:' , error ) ;
150
- cleanupAudio ( true ) ;
128
+ cleanupAudio ( ) ;
151
129
} ) ;
152
130
153
131
socketRef . current . on ( 'signal' , async ( data : SignalData ) => {
132
+ console . log ( 'Received signal data:' , data ) ;
133
+
154
134
if ( data . type === 'offer' && ! peerRef . current ) {
155
135
try {
156
136
const stream = await navigator . mediaDevices . getUserMedia ( {
@@ -167,7 +147,7 @@ const AudioSharing = () => {
167
147
peerRef . current = createPeer ( stream , false ) ;
168
148
} catch ( error ) {
169
149
console . error ( 'Error accessing audio devices:' , error ) ;
170
- cleanupAudio ( true ) ;
150
+ cleanupAudio ( ) ;
171
151
}
172
152
}
173
153
@@ -176,7 +156,7 @@ const AudioSharing = () => {
176
156
peerRef . current . signal ( data as SimplePeer . SignalData ) ;
177
157
} catch ( error ) {
178
158
console . error ( 'Error signaling peer:' , error ) ;
179
- cleanupAudio ( true ) ;
159
+ cleanupAudio ( ) ;
180
160
}
181
161
}
182
162
} ) ;
@@ -211,39 +191,10 @@ const AudioSharing = () => {
211
191
}
212
192
} catch ( error ) {
213
193
console . error ( 'Error toggling audio:' , error ) ;
214
- cleanupAudio ( true ) ;
194
+ cleanupAudio ( ) ;
215
195
}
216
196
} ;
217
197
218
- // Mount/unmount handling
219
- useEffect ( ( ) => {
220
- mountCountRef . current ++ ;
221
- console . log ( `Component mounted (count: ${ mountCountRef . current } )` ) ;
222
-
223
- // Only do full cleanup when actually leaving the page
224
- return ( ) => {
225
- mountCountRef . current -- ;
226
- console . log ( `Component unmounting (count: ${ mountCountRef . current } )` ) ;
227
-
228
- // If this is the last mount point, do a full cleanup
229
- if ( mountCountRef . current === 0 ) {
230
- cleanupAudio ( true ) ;
231
- }
232
- } ;
233
- } , [ ] ) ;
234
-
235
- // Handle page unload
236
- useEffect ( ( ) => {
237
- const handleUnload = ( ) => {
238
- cleanupAudio ( true ) ;
239
- } ;
240
-
241
- window . addEventListener ( 'beforeunload' , handleUnload ) ;
242
- return ( ) => {
243
- window . removeEventListener ( 'beforeunload' , handleUnload ) ;
244
- } ;
245
- } , [ ] ) ;
246
-
247
198
return (
248
199
< div className = "flex items-center gap-4" >
249
200
< button
0 commit comments