![]() Server : Apache/2 System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64 User : gositeme ( 1004) PHP Version : 8.2.29 Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname Directory : /home/gositeme/domains/lavocat.ca/public_html/src/components/ |
import React, { useEffect, useState, useRef } from 'react';
import { useWebSocket } from '../context/StableWebSocketContext';
import { motion, AnimatePresence } from 'framer-motion';
import { Phone, PhoneOff, Video, Check, X } from 'lucide-react';
const IncomingCallNotification: React.FC = () => {
const { incomingVideoCall, acceptVideoCall, declineVideoCall, videoCallActive } = useWebSocket();
const [isRinging, setIsRinging] = useState(false);
const [isHandled, setIsHandled] = useState(false);
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
// Start ringing animation when call comes in
useEffect(() => {
if (incomingVideoCall && !isHandled) {
setIsRinging(true);
// Auto-decline after 30 seconds
timeoutRef.current = setTimeout(() => {
console.log('📞 [DEBUG] Auto-declining call after 30 seconds');
setIsHandled(true);
setIsRinging(false);
declineVideoCall();
}, 30000);
} else {
setIsRinging(false);
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
}
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
};
}, [incomingVideoCall, isHandled, declineVideoCall]);
// ✅ Listen for call cancellation events
useEffect(() => {
const handleCallCancelled = (event: CustomEvent) => {
const { senderId, senderName, reason } = event.detail;
console.log('📞 [DEBUG] Received call cancellation:', { senderId, senderName, reason });
// If there's an active incoming call from this sender, dismiss it
if (incomingVideoCall && incomingVideoCall.senderId === senderId) {
console.log('📞 [DEBUG] Dismissing incoming call notification due to cancellation');
setIsHandled(true);
setIsRinging(false);
// Clear the timeout
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
}
};
window.addEventListener('video-call-cancelled', handleCallCancelled as EventListener);
return () => {
window.removeEventListener('video-call-cancelled', handleCallCancelled as EventListener);
};
}, [incomingVideoCall]);
const handleAccept = () => {
if (incomingVideoCall && !isHandled) {
console.log('📞 [SIMPLE] YES pressed - accepting call');
setIsHandled(true);
setIsRinging(false);
// Clear the timeout
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
// ✅ DEAD SIMPLE: Just call the accept function
// The WebSocket context will handle EVERYTHING
acceptVideoCall(incomingVideoCall);
console.log('📞 [SIMPLE] Done - video call should be starting');
}
};
const handleDecline = () => {
if (incomingVideoCall && !isHandled) {
setIsHandled(true);
setIsRinging(false);
// Clear the timeout
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
// Notify the caller that the call was rejected
window.dispatchEvent(new CustomEvent('video-call-rejected', {
detail: {
senderId: incomingVideoCall.senderId,
senderName: incomingVideoCall.senderName
}
}));
declineVideoCall();
}
};
// ✅ Don't show notification if video call is already active
if (!incomingVideoCall || videoCallActive) return null;
return (
<AnimatePresence>
<motion.div
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.8 }}
className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50"
>
<motion.div
initial={{ opacity: 0, y: -50 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -50 }}
className="bg-white dark:bg-gray-900 rounded-2xl shadow-2xl p-8 max-w-md w-full mx-4 text-center"
>
{/* Video call icon */}
<motion.div
animate={isRinging ? { scale: [1, 1.1, 1] } : {}}
transition={{ duration: 1, repeat: Infinity }}
className="w-20 h-20 bg-blue-500 rounded-full flex items-center justify-center mx-auto mb-6"
>
<Video className="h-10 w-10 text-white" />
</motion.div>
{/* Call info */}
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-2">
📞 Incoming Video Call
</h2>
<p className="text-xl text-gray-600 dark:text-gray-300 mb-8">
<strong>{incomingVideoCall.senderName}</strong> is calling you
</p>
{/* Large Yes/No buttons */}
<div className="flex gap-6 justify-center">
{/* NO Button */}
<motion.button
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
onClick={handleDecline}
className="flex flex-col items-center gap-3 px-8 py-6 bg-red-500 hover:bg-red-600 text-white rounded-2xl font-bold text-xl transition-colors min-w-[120px]"
>
<X className="h-8 w-8" />
NO
</motion.button>
{/* YES Button */}
<motion.button
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
onClick={handleAccept}
className="flex flex-col items-center gap-3 px-8 py-6 bg-green-500 hover:bg-green-600 text-white rounded-2xl font-bold text-xl transition-colors min-w-[120px]"
>
<Check className="h-8 w-8" />
YES
</motion.button>
</div>
{/* Timer */}
<div className="mt-6">
<p className="text-sm text-gray-500 dark:text-gray-400">
⏰ Auto-declines in 30 seconds
</p>
</div>
</motion.div>
</motion.div>
</AnimatePresence>
);
};
export default IncomingCallNotification;