diff --git a/hooks/use-rocketchat-calls.ts b/hooks/use-rocketchat-calls.ts index 128ee30..04ec119 100644 --- a/hooks/use-rocketchat-calls.ts +++ b/hooks/use-rocketchat-calls.ts @@ -122,11 +122,20 @@ export function useRocketChatCalls() { from: callEvent.from.username, }); - // Only remove notification if it matches the current call's roomId - // This prevents removing notifications from other calls - if (currentCallRoomIdRef.current === callEvent.roomId || !currentCallRoomIdRef.current) { + // Remove notification if: + // 1. It matches the current call's roomId, OR + // 2. The call-ended event has no roomId (generic end event), OR + // 3. There's no current call tracked (shouldn't happen, but safety check) + const shouldRemove = + currentCallRoomIdRef.current === callEvent.roomId || + !callEvent.roomId || // Generic call-ended event (no roomId) - match any active call + !currentCallRoomIdRef.current; + + if (shouldRemove) { console.log('[useRocketChatCalls] ✅ Removing notification for ended call', { roomId: callEvent.roomId, + currentCallRoomId: currentCallRoomIdRef.current, + isGenericEnd: !callEvent.roomId, }); // Clear the safety timeout since we received the call-ended event @@ -176,11 +185,12 @@ export function useRocketChatCalls() { // Set a safety timeout to clear the notification if no call-ended event is received // This handles cases where RocketChat doesn't send the call-ended event + // Reduced to 45 seconds - if the call hasn't ended by then, it's likely the caller hung up if (callTimeoutRef.current) { clearTimeout(callTimeoutRef.current); } callTimeoutRef.current = setTimeout(() => { - console.log('[useRocketChatCalls] ⏰ Safety timeout: clearing call notification after 2 minutes', { + console.log('[useRocketChatCalls] ⏰ Safety timeout: clearing call notification after 45 seconds', { roomId: callEvent.roomId, }); if (currentCallRoomIdRef.current === callEvent.roomId) { @@ -188,7 +198,7 @@ export function useRocketChatCalls() { currentCallRoomIdRef.current = null; } callTimeoutRef.current = null; - }, 120000); // 2 minutes safety timeout + }, 45000); // 45 seconds safety timeout (reduced from 2 minutes) console.log('[useRocketChatCalls] 📞 Incoming call notification UI set', { from: callEvent.from.username, diff --git a/lib/services/rocketchat-call-listener.ts b/lib/services/rocketchat-call-listener.ts index ca77482..cacd164 100644 --- a/lib/services/rocketchat-call-listener.ts +++ b/lib/services/rocketchat-call-listener.ts @@ -602,6 +602,7 @@ export class RocketChatCallListener { }); // Check if this is a call ending event (hangup, end, cancel, reject, etc.) + // Also check for various RocketChat-specific call end indicators const isCallEnded = callType === 'call-ended' || callType === 'hangup' || @@ -609,16 +610,27 @@ export class RocketChatCallListener { callType === 'cancel' || callType === 'reject' || callType === 'decline' || + callType === 'timeout' || + callType === 'missed' || eventData.action === 'hangup' || eventData.action === 'end' || eventData.action === 'cancel' || eventData.action === 'reject' || eventData.action === 'decline' || + eventData.action === 'timeout' || eventData.status === 'ended' || eventData.status === 'cancelled' || eventData.status === 'rejected' || + eventData.status === 'timeout' || + eventData.status === 'missed' || eventData.event === 'hangup' || - eventData.event === 'end'; + eventData.event === 'end' || + eventData.event === 'timeout' || + eventData.message?.action === 'end' || + eventData.message?.action === 'hangup' || + eventData.message?.action === 'cancel' || + // Check if message type indicates call ended + (eventData.message?.t === 'videoconf' && (eventData.message?.action === 'end' || eventData.message?.status === 'ended')); // Check if this is an incoming call event // RocketChat sends calls via notifications with message.t === 'videoconf' or 'audio' @@ -637,38 +649,76 @@ export class RocketChatCallListener { (eventData.type === 'call' && eventData.status === 'ringing'); // Handle call ended events - if (isCallEnded && roomId) { - const callEvent: CallEvent = { - type: 'call-ended', - from: { - userId: from._id || from.userId || from.id || '', - username: from.username || from.name || 'Unknown', - name: from.name || from.username || 'Unknown', - }, - roomId, - roomName: roomName || from.name || 'Chat', - timestamp: new Date(), - }; + // Also handle cases where roomId might not be present but we have other indicators + if (isCallEnded) { + // Try to extract roomId from various sources if not directly available + const finalRoomId = roomId || eventData.rid || eventData.roomId || eventData.room?._id || ''; + + if (finalRoomId) { + const callEvent: CallEvent = { + type: 'call-ended', + from: { + userId: from._id || from.userId || from.id || '', + username: from.username || from.name || 'Unknown', + name: from.name || from.username || 'Unknown', + }, + roomId: finalRoomId, + roomName: roomName || from.name || 'Chat', + timestamp: new Date(), + }; - logger.info('[ROCKETCHAT_CALL_LISTENER] 📞 CALL ENDED DETECTED!', { - from: callEvent.from.username, - roomId: callEvent.roomId, - roomName: callEvent.roomName, - callType, - action: eventData.action, - status: eventData.status, - }); + logger.info('[ROCKETCHAT_CALL_LISTENER] 📞 CALL ENDED DETECTED!', { + from: callEvent.from.username, + roomId: callEvent.roomId, + roomName: callEvent.roomName, + callType, + action: eventData.action, + status: eventData.status, + event: eventData.event, + messageAction: eventData.message?.action, + }); - // Notify all handlers that the call has ended - this.callHandlers.forEach((handler) => { - try { - handler(callEvent); - } catch (error) { - logger.error('[ROCKETCHAT_CALL_LISTENER] Error in call ended handler', { - error: error instanceof Error ? error.message : String(error) - }); - } - }); + // Notify all handlers that the call has ended + this.callHandlers.forEach((handler) => { + try { + handler(callEvent); + } catch (error) { + logger.error('[ROCKETCHAT_CALL_LISTENER] Error in call ended handler', { + error: error instanceof Error ? error.message : String(error) + }); + } + }); + } else { + // Even without roomId, if we're sure it's a call end event, try to match by any active call + logger.info('[ROCKETCHAT_CALL_LISTENER] 📞 CALL ENDED DETECTED (no roomId, will match any active call)', { + callType, + action: eventData.action, + status: eventData.status, + }); + + // Create a generic call-ended event that will match any active call + const callEvent: CallEvent = { + type: 'call-ended', + from: { + userId: from._id || from.userId || from.id || '', + username: from.username || from.name || 'Unknown', + name: from.name || from.username || 'Unknown', + }, + roomId: '', // Empty roomId will match any call in the handler + roomName: roomName || from.name || 'Chat', + timestamp: new Date(), + }; + + this.callHandlers.forEach((handler) => { + try { + handler(callEvent); + } catch (error) { + logger.error('[ROCKETCHAT_CALL_LISTENER] Error in call ended handler', { + error: error instanceof Error ? error.message : String(error) + }); + } + }); + } } // Handle incoming call events else if (isIncomingCall && roomId) {