diff --git a/components/incoming-call-notification.tsx b/components/incoming-call-notification.tsx
new file mode 100644
index 0000000..d8e06ea
--- /dev/null
+++ b/components/incoming-call-notification.tsx
@@ -0,0 +1,130 @@
+"use client";
+
+import { useState, useEffect } from 'react';
+import { Phone, PhoneOff, X } from 'lucide-react';
+import { Button } from '@/components/ui/button';
+import { useRouter } from 'next/navigation';
+
+export interface IncomingCall {
+ from: {
+ userId: string;
+ username: string;
+ name: string;
+ };
+ roomId: string;
+ roomName: string;
+ timestamp: Date;
+}
+
+interface IncomingCallNotificationProps {
+ call: IncomingCall | null;
+ onDismiss: () => void;
+ onAccept: (roomId: string) => void;
+ onReject: () => void;
+}
+
+export function IncomingCallNotification({
+ call,
+ onDismiss,
+ onAccept,
+ onReject,
+}: IncomingCallNotificationProps) {
+ const router = useRouter();
+ const [isVisible, setIsVisible] = useState(false);
+
+ useEffect(() => {
+ if (call) {
+ setIsVisible(true);
+
+ // Auto-dismiss after 30 seconds if user doesn't interact
+ const autoDismissTimer = setTimeout(() => {
+ setIsVisible(false);
+ onDismiss();
+ }, 30000); // 30 seconds
+
+ return () => {
+ clearTimeout(autoDismissTimer);
+ };
+ } else {
+ setIsVisible(false);
+ }
+ }, [call, onDismiss]);
+
+ if (!call || !isVisible) {
+ return null;
+ }
+
+ const handleAccept = () => {
+ onAccept(call.roomId);
+ // Navigate to parole page with room ID
+ router.push(`/parole?room=${call.roomId}`);
+ setIsVisible(false);
+ };
+
+ const handleReject = () => {
+ onReject();
+ setIsVisible(false);
+ };
+
+ const handleDismiss = () => {
+ onDismiss();
+ setIsVisible(false);
+ };
+
+ const callerName = call.from.name || call.from.username || 'Inconnu';
+
+ return (
+
+
+ {/* Header with Outlook-like style */}
+
+
+
+
+
Parole
+
Appel entrant
+
+
+
+
+
+ {/* Call Info - Outlook style message */}
+
+
+ Vous avez un appel de {callerName}
+
+ {call.roomName && call.roomName !== callerName && (
+
Dans {call.roomName}
+ )}
+
+
+ {/* Actions - Outlook style buttons */}
+
+
+
+
+
+
+ );
+}
diff --git a/components/layout/layout-wrapper.tsx b/components/layout/layout-wrapper.tsx
index 77ad7f0..3d234a6 100644
--- a/components/layout/layout-wrapper.tsx
+++ b/components/layout/layout-wrapper.tsx
@@ -9,6 +9,7 @@ import { Toaster } from "@/components/ui/toaster";
import { useBackgroundImage } from "@/components/background-switcher";
import { clearAuthCookies, clearKeycloakCookies } from "@/lib/session";
import { useRocketChatCalls } from "@/hooks/use-rocketchat-calls";
+import { IncomingCallNotification } from "@/components/incoming-call-notification";
interface LayoutWrapperProps {
children: React.ReactNode;
@@ -206,6 +207,23 @@ export function LayoutWrapper({ children, isSignInPage, isAuthenticated }: Layou
{!isSignInPage && isAuthenticated && }
+
+ {/* Incoming call notification */}
+ {!isSignInPage && isAuthenticated && (
+ setIncomingCall(null)}
+ onAccept={(roomId) => {
+ console.log('[LayoutWrapper] Call accepted, navigating to room:', roomId);
+ setIncomingCall(null);
+ }}
+ onReject={() => {
+ console.log('[LayoutWrapper] Call rejected');
+ setIncomingCall(null);
+ // TODO: Send reject signal to RocketChat if needed
+ }}
+ />
+ )}
);
}
\ No newline at end of file
diff --git a/hooks/use-rocketchat-calls.ts b/hooks/use-rocketchat-calls.ts
index 8efa258..680ac8a 100644
--- a/hooks/use-rocketchat-calls.ts
+++ b/hooks/use-rocketchat-calls.ts
@@ -1,8 +1,9 @@
-import { useEffect, useRef, useCallback } from 'react';
+import { useEffect, useRef, useCallback, useState } from 'react';
import { useSession } from 'next-auth/react';
import { RocketChatCallListener, CallEvent } from '@/lib/services/rocketchat-call-listener';
import { useWidgetNotification } from './use-widget-notification';
import { logger } from '@/lib/logger';
+import { IncomingCall } from '@/components/incoming-call-notification';
/**
* Hook to listen for incoming RocketChat calls and trigger notifications
@@ -13,6 +14,7 @@ export function useRocketChatCalls() {
const listenerRef = useRef(null);
const unsubscribeRef = useRef<(() => void) | null>(null);
const initializedRef = useRef(false);
+ const [incomingCall, setIncomingCall] = useState(null);
/**
* Get RocketChat credentials for the current user
@@ -176,4 +178,9 @@ export function useRocketChatCalls() {
initializedRef.current = false;
};
}, [session?.user?.id, initializeListener]);
+
+ return {
+ incomingCall,
+ setIncomingCall,
+ };
}
diff --git a/lib/services/rocketchat-call-listener.ts b/lib/services/rocketchat-call-listener.ts
index 59b455e..5dcb3a7 100644
--- a/lib/services/rocketchat-call-listener.ts
+++ b/lib/services/rocketchat-call-listener.ts
@@ -382,10 +382,14 @@ export class RocketChatCallListener {
const notification = args[0];
const payload = notification.payload;
- // Check if it's a videoconf (video call) message
- if (payload?.message?.t === 'videoconf' || payload?.message?.t === 'audio' || payload?.message?.t === 'video') {
+ // Only process call-related notifications (videoconf, audio, video)
+ // Ignore system notifications like version updates, etc.
+ const messageType = payload?.message?.t;
+ const isCallNotification = messageType === 'videoconf' || messageType === 'audio' || messageType === 'video';
+
+ if (isCallNotification) {
console.log('[ROCKETCHAT_CALL_LISTENER] ✅ VIDEO/AUDIO CALL DETECTED in notification!', {
- type: payload.message.t,
+ type: messageType,
sender: payload.sender,
roomId: payload.rid,
});
@@ -399,22 +403,31 @@ export class RocketChatCallListener {
roomName: payload.name || notification.title,
message: payload.message,
});
+ } else {
+ // Log ignored notifications for debugging (but don't process them)
+ console.log('[ROCKETCHAT_CALL_LISTENER] ⏭️ Ignoring non-call notification', {
+ messageType,
+ notificationId: payload?._id || notification?.id,
+ title: notification?.title || payload?.title,
+ });
}
}
- // Check if this is a webrtc event
+ // Check if this is a webrtc event (only process if it looks like an incoming call)
if (eventName?.includes('/webrtc')) {
console.log('[ROCKETCHAT_CALL_LISTENER] ✅ This is a webrtc event!');
if (args.length > 0) {
- this.handleCallEvent(args[0]);
- } else {
- // Sometimes the event data might be in the message itself
- this.handleCallEvent(message.fields);
+ const webrtcData = args[0];
+ // Only process if it's an incoming call (ringing, offer, etc.)
+ if (webrtcData.action === 'ringing' || webrtcData.type === 'call' || webrtcData.event === 'incoming') {
+ this.handleCallEvent(webrtcData);
+ }
}
}
- // Also check for other possible call-related events
- if (eventName?.includes('/call') || eventName?.includes('/video') || eventName?.includes('/audio')) {
+ // Also check for other possible call-related events (but be more specific)
+ // Only process if the event name explicitly indicates a call
+ if (eventName?.includes('/call') && (eventName?.includes('/incoming') || eventName?.includes('/ringing'))) {
console.log('[ROCKETCHAT_CALL_LISTENER] ✅ This might be a call event!');
if (args.length > 0) {
this.handleCallEvent(args[0]);